Commit bd1ad5b6 authored by sendtion's avatar sendtion

Initial commit

parents
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
<entry name="!?*.aj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>
\ No newline at end of file
<component name="CopyrightManager">
<settings default="" />
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/xrichtext" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<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$/xrichtext/xrichtext.iml" filepath="$PROJECT_DIR$/xrichtext/xrichtext.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>
\ No newline at end of file
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.0"
defaultConfig {
applicationId "com.sendtion.xrichtext"
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
testCompile 'junit:junit:4.12'
compile project(':xrichtext')
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.android.support:design:24.2.1'
compile 'com.android.support:cardview-v7:24.2.1'
//recyclerview列表
compile 'com.jcodecraeer:xrecyclerview:1.2.7'
}
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:\Android\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
package com.sendtion.xrichtextdemo;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.sendtion.xrichtext", appContext.getPackageName());
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sendtion.xrichtextdemo">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".ui.MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.NewActivity"
android:label="@string/title_activity_new"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".ui.NoteActivity"
android:label="@string/title_activity_note"
android:theme="@style/AppTheme.NoActionBar"></activity>
</application>
</manifest>
\ No newline at end of file
package com.sendtion.xrichtextdemo.adapter;
import android.content.Context;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.sendtion.xrichtextdemo.R;
import com.sendtion.xrichtextdemo.bean.Note;
import java.util.ArrayList;
import java.util.List;
/**
* 作者:Sendtion on 2016/10/21 0021 16:59
* 邮箱:sendtion@163.com
* 博客:http://sendtion.cn
* 描述:笔记列表适配器
*/
public class MyNoteListAdapter extends RecyclerView.Adapter<MyNoteListAdapter.ViewHolder>
implements View.OnClickListener, View.OnLongClickListener {
private Context mContext;
private List<Note> mNotes;
private OnRecyclerViewItemClickListener mOnItemClickListener ;
private OnRecyclerViewItemLongClickListener mOnItemLongClickListener ;
public MyNoteListAdapter() {
mNotes = new ArrayList<>();
}
public void setmNotes(List<Note> notes) {
this.mNotes = notes;
}
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
//注意这里使用getTag方法获取数据
mOnItemClickListener.onItemClick(v,(Note)v.getTag());
}
}
@Override
public boolean onLongClick(View v) {
if (mOnItemLongClickListener != null) {
//注意这里使用getTag方法获取数据
mOnItemLongClickListener.onItemLongClick(v,(Note)v.getTag());
}
return true;
}
public interface OnRecyclerViewItemClickListener {
void onItemClick(View view , Note note);
}
public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
this.mOnItemClickListener = listener;
}
public interface OnRecyclerViewItemLongClickListener {
void onItemLongClick(View view , Note note);
}
public void setOnItemLongClickListener(OnRecyclerViewItemLongClickListener listener) {
this.mOnItemLongClickListener = listener;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//Log.i(TAG, "###onCreateViewHolder: ");
//inflate(R.layout.list_item_record,parent,false) 如果不这么写,cardview不能适应宽度
mContext = parent.getContext();
View view = LayoutInflater.from(mContext).inflate(R.layout.list_item_note,parent,false);
//将创建的View注册点击事件
view.setOnClickListener(this);
view.setOnLongClickListener(this);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
//Log.i(TAG, "###onBindViewHolder: ");
final Note note = mNotes.get(position);
//将数据保存在itemView的Tag中,以便点击时进行获取
holder.itemView.setTag(note);
//Log.e("adapter", "###record="+record);
holder.tv_list_title.setText(note.getTitle());
holder.tv_list_summary.setText(note.getContent());
holder.tv_list_time.setText(note.getCreateTime());
holder.tv_list_group.setText(note.getGroupName());
}
@Override
public int getItemCount() {
//Log.i(TAG, "###getItemCount: ");
if (mNotes != null && mNotes.size()>0){
return mNotes.size();
}
return 0;
}
//自定义的ViewHolder,持有每个Item的的所有界面元素
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView tv_list_title;//笔记标题
public TextView tv_list_summary;//笔记摘要
public TextView tv_list_time;//创建时间
public TextView tv_list_group;//笔记分类
public CardView card_view_note;
public ViewHolder(View view){
super(view);
card_view_note = (CardView) view.findViewById(R.id.card_view_note);
tv_list_title = (TextView) view.findViewById(R.id.tv_list_title);
tv_list_summary = (TextView) view.findViewById(R.id.tv_list_summary);
tv_list_time = (TextView) view.findViewById(R.id.tv_list_time);
tv_list_group = (TextView) view.findViewById(R.id.tv_list_group);
}
}
}
package com.sendtion.xrichtextdemo.bean;
/**
* 作者:Sendtion on 2016/10/24 0024 15:05
* 邮箱:sendtion@163.com
* 博客:http://sendtion.cn
* 描述:笔记分类
*/
public class Group {
private int id;//ID
private String name;//分组名称
private int order;//排列顺序
private String color;//分类颜色,存储颜色代码
private int isEncrypt ;//是否加密,0未加密,1加密
private String createTime;//创建时间
private String updateTime;//修改时间
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getIsEncrypt() {
return isEncrypt;
}
public void setIsEncrypt(int isEncrypt) {
this.isEncrypt = isEncrypt;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public String getUpdateTime() {
return updateTime;
}
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
}
package com.sendtion.xrichtextdemo.bean;
import java.io.Serializable;
/**
* 作者:Sendtion on 2016/10/24 0024 15:00
* 邮箱:sendtion@163.com
* 博客:http://sendtion.cn
* 描述:笔记实体类
*/
public class Note implements Serializable {
private int id;//笔记ID
private String title;//笔记标题
private String content;//笔记内容
private int groupId;//分类ID
private String groupName;//分类名称
private int type;//笔记类型,1纯文本,2Html,3Markdown
private String bgColor;//背景颜色,存储颜色代码
private int isEncrypt ;//是否加密,0未加密,1加密
private String createTime;//创建时间
private String updateTime;//修改时间
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getGroupId() {
return groupId;
}
public void setGroupId(int groupId) {
this.groupId = groupId;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getBgColor() {
return bgColor;
}
public void setBgColor(String bgColor) {
this.bgColor = bgColor;
}
public int getIsEncrypt() {
return isEncrypt;
}
public void setIsEncrypt(int isEncrypt) {
this.isEncrypt = isEncrypt;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public String getUpdateTime() {
return updateTime;
}
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
}
package com.sendtion.xrichtextdemo.db;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import com.sendtion.xrichtextdemo.bean.Group;
import com.sendtion.xrichtextdemo.util.DateUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static android.content.ContentValues.TAG;
/**
* 作者:Sendtion on 2016/10/24 0024 17:18
* 邮箱:sendtion@163.com
* 博客:http://sendtion.cn
* 描述:分类处理
*/
public class GroupDao {
private MyOpenHelper helper;
private NoteDao noteDataDao;
public GroupDao(Context context) {
helper = new MyOpenHelper(context);
noteDataDao = new NoteDao(context);
}
/**
* 查询所有分类列表
*
* @return
*/
public List<Group> queryGroupAll() {
SQLiteDatabase db = helper.getWritableDatabase();
List<Group> groupList = new ArrayList<Group>();
Group group ;
Cursor cursor = null;
try {
cursor = db.query("db_group", null, null, null, null, null, "g_create_time asc");
while (cursor.moveToNext()) {
int groupId = cursor.getInt(cursor.getColumnIndex("g_id"));
String groupName = cursor.getString(cursor.getColumnIndex("g_name"));
int order = cursor.getInt(cursor.getColumnIndex("g_order"));
String color = cursor.getString(cursor.getColumnIndex("g_color"));
int encrypt = cursor.getInt(cursor.getColumnIndex("g_encrypt"));
String createTime = cursor.getString(cursor.getColumnIndex("g_create_time"));
String updateTime = cursor.getString(cursor.getColumnIndex("g_update_time"));
//生成一个分类
group = new Group();
group.setId(groupId);
group.setName(groupName);
group.setOrder(order);
group.setColor(color);
group.setIsEncrypt(encrypt);
group.setCreateTime(createTime);
group.setUpdateTime(updateTime);
groupList.add(group);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
if (db != null) {
db.close();
}
}
return groupList;
}
/**
* 根据分类名查询分类
*
* @param groupName
* @return
*/
public Group queryGroupByName(String groupName) {
SQLiteDatabase db = helper.getWritableDatabase();
Group group = null;
Cursor cursor = null;
try {
Log.i(TAG, "###queryGroupByName: "+groupName);
cursor = db.query("db_group", null, "g_name=?", new String[]{groupName}, null, null, null);
while (cursor.moveToNext()) {
int groupId = cursor.getInt(cursor.getColumnIndex("g_id"));
int order = cursor.getInt(cursor.getColumnIndex("g_order"));
String color = cursor.getString(cursor.getColumnIndex("g_color"));
int encrypt = cursor.getInt(cursor.getColumnIndex("g_encrypt"));
String createTime = cursor.getString(cursor.getColumnIndex("g_create_time"));
String updateTime = cursor.getString(cursor.getColumnIndex("g_update_time"));
//生成一个分类
group = new Group();
group.setId(groupId);
group.setName(groupName);
group.setOrder(order);
group.setColor(color);
group.setIsEncrypt(encrypt);
group.setCreateTime(createTime);
group.setUpdateTime(updateTime);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
if (db != null) {
db.close();
}
}
return group;
}
/**
* 根据分类ID查询分类
*
* @return
*/
public Group queryGroupById(int groupId) {
SQLiteDatabase db = helper.getWritableDatabase();
Group group = null;
Cursor cursor = null;
try {
cursor = db.query("db_group", null, "g_id=?", new String[]{groupId + ""}, null, null, null);
while (cursor.moveToNext()) {
int order = cursor.getInt(cursor.getColumnIndex("g_order"));
String color = cursor.getString(cursor.getColumnIndex("g_color"));
int encrypt = cursor.getInt(cursor.getColumnIndex("g_encrypt"));
String groupName = cursor.getString(cursor.getColumnIndex("g_name"));
String createTime = cursor.getString(cursor.getColumnIndex("g_create_time"));
String updateTime = cursor.getString(cursor.getColumnIndex("g_update_time"));
//生成一个订单
group = new Group();
group.setId(groupId);
group.setName(groupName);
group.setOrder(order);
group.setColor(color);
group.setIsEncrypt(encrypt);
group.setCreateTime(createTime);
group.setUpdateTime(updateTime);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
if (db != null) {
db.close();
}
}
return group;
}
/**
* 添加一个分类
*/
public void insertGroup(String groupName) {
SQLiteDatabase db = helper.getWritableDatabase();
Cursor cursor = null;
try {
cursor = db.query("group", null, "g_name=?", new String[]{groupName}, null, null, null);
if (!cursor.moveToNext()) {//如果订单不存在
ContentValues values = new ContentValues();
values.put("g_name", groupName);
values.put("g_color", "#FFFFFF");
values.put("g_encrypt", 0);
values.put("g_create_time", DateUtils.date2string(new Date()));
values.put("g_update_time", DateUtils.date2string(new Date()));
db.insert("db_group", null, values);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
if (db != null) {
db.close();
}
}
}
/**
* 更新一个分类
*/
public void updateGroup(Group group) {
SQLiteDatabase db = helper.getWritableDatabase();
try {
ContentValues values = new ContentValues();
values.put("g_name", group.getName());
values.put("g_order", group.getOrder());
values.put("g_color", group.getColor());
values.put("g_encrypt", group.getIsEncrypt());
values.put("update_time", DateUtils.date2string(new Date()));
db.update("db_group", values, "g_id=?", new String[]{group.getId() + ""});
} catch (Exception e) {
e.printStackTrace();
} finally {
if (db != null) {
db.close();
}
}
}
/**
* 删除一个分类
*/
public int deleteGroup(int groupId) {
SQLiteDatabase db = helper.getWritableDatabase();
int ret = 0;
try {
ret = db.delete("db_group", "g_id=?", new String[]{groupId + ""});
//Group group = queryGroupByName("默认笔记");
//noteDataDao.updateNote2(groupId, group.getGroupId());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (db != null) {
db.close();
}
}
return ret;
}
}
package com.sendtion.xrichtextdemo.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.sendtion.xrichtextdemo.util.DateUtils;
import java.util.Date;
/**
* 作者:Sendtion on 2016/10/24 0024 15:14
* 邮箱:sendtion@163.com
* 博客:http://sendtion.cn
* 描述:数据库帮助类
*/
public class MyOpenHelper extends SQLiteOpenHelper {
private final static String DB_NAME = "note.db";// 数据库文件名
private final static int DB_VERSION = 1;// 数据库版本
public MyOpenHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
//创建分类表
db.execSQL("create table db_group(g_id integer primary key autoincrement, " +
"g_name varchar, g_order integer, g_color varchar, g_encrypt integer," +
"g_create_time datetime, g_update_time datetime )");
//创建笔记表
db.execSQL("create table db_note(n_id integer primary key autoincrement, n_title varchar, " +
"n_content varchar, n_group_id integer, n_group_name varchar, n_type integer, " +
"n_bg_color varchar, n_encrypt integer, n_create_time datetime," +
"n_update_time datetime )");
db.execSQL("insert into db_group(g_name, g_order, g_color, g_encrypt, g_create_time, g_update_time) " +
"values(?,?,?,?,?,?)", new String[]{"默认笔记", "1", "#FFFFFF", "0", DateUtils.date2string(new Date()),DateUtils.date2string(new Date())});
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
package com.sendtion.xrichtextdemo.db;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import com.sendtion.xrichtextdemo.bean.Note;
import com.sendtion.xrichtextdemo.util.DateUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 作者:Sendtion on 2016/10/24 0024 15:53
* 邮箱:sendtion@163.com
* 博客:http://sendtion.cn
* 描述:笔记处理
*/
public class NoteDao {
private MyOpenHelper helper;
public NoteDao(Context context) {
helper = new MyOpenHelper(context);
}
/**
* 查询所有笔记
*/
public List<Note> queryNotesAll(int groupId) {
SQLiteDatabase db = helper.getWritableDatabase();
List<Note> noteList = new ArrayList<>();
Note note ;
String sql ;
Cursor cursor = null;
try {
if (groupId > 0){
sql = "select * from db_note where n_group_id =" + groupId +
"order by n_create_time desc";
} else {
sql = "select * from db_note " ;
}
cursor = db.rawQuery(sql, null);
//cursor = db.query("note", null, null, null, null, null, "n_id desc");
while (cursor.moveToNext()) {
//循环获得展品信息
note = new Note();
note.setId(cursor.getInt(cursor.getColumnIndex("n_id")));
note.setTitle(cursor.getString(cursor.getColumnIndex("n_title")));
note.setContent(cursor.getString(cursor.getColumnIndex("n_content")));
note.setGroupId(cursor.getInt(cursor.getColumnIndex("n_group_id")));
note.setGroupName(cursor.getString(cursor.getColumnIndex("n_group_name")));
note.setType(cursor.getInt(cursor.getColumnIndex("n_type")));
note.setBgColor(cursor.getString(cursor.getColumnIndex("n_bg_color")));
note.setIsEncrypt(cursor.getInt(cursor.getColumnIndex("n_encrypt")));
note.setCreateTime(cursor.getString(cursor.getColumnIndex("n_create_time")));
note.setUpdateTime(cursor.getString(cursor.getColumnIndex("n_update_time")));
noteList.add(note);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
if (db != null) {
db.close();
}
}
return noteList;
}
/**
* 插入笔记
*/
public long insertNote(Note note) {
SQLiteDatabase db = helper.getWritableDatabase();
String sql = "insert into db_note(n_title,n_content,n_group_id,n_group_name," +
"n_type,n_bg_color,n_encrypt,n_create_time,n_update_time) " +
"values(?,?,?,?,?,?,?,?,?)";
long ret = 0;
//sql = "insert into ex_user(eu_login_name,eu_create_time,eu_update_time) values(?,?,?)";
SQLiteStatement stat = db.compileStatement(sql);
db.beginTransaction();
try {
stat.bindString(1, note.getTitle());
stat.bindString(2, note.getContent());
stat.bindLong(3, note.getGroupId());
stat.bindString(4, note.getGroupName());
stat.bindLong(5, note.getType());
stat.bindString(6, note.getBgColor());
stat.bindLong(7, note.getIsEncrypt());
stat.bindString(8, DateUtils.date2string(new Date()));
stat.bindString(9, DateUtils.date2string(new Date()));
ret = stat.executeInsert();
db.setTransactionSuccessful();
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
db.endTransaction();
db.close();
}
return ret;
}
/**
* 更新笔记
* @param note
*/
public void updateNote(Note note) {
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("n_title", note.getTitle());
values.put("n_content", note.getContent());
values.put("n_group_id", note.getGroupId());
values.put("n_group_name", note.getGroupName());
values.put("n_type", note.getType());
values.put("n_bg_color", note.getBgColor());
values.put("n_encrypt", note.getIsEncrypt());
values.put("n_update_time", DateUtils.date2string(new Date()));
db.update("db_note", values, "n_id=?", new String[]{note.getId()+""});
db.close();
}
/**
* 删除笔记
*/
public int deleteNote(int noteId) {
SQLiteDatabase db = helper.getWritableDatabase();
int ret = 0;
try {
ret = db.delete("db_note", "n_id=?", new String[]{noteId + ""});
} catch (Exception e) {
e.printStackTrace();
} finally {
if (db != null) {
db.close();
}
}
return ret;
}
/**
* 批量删除笔记
*
* @param mNotes
*/
public int deleteNote(List<Note> mNotes) {
SQLiteDatabase db = helper.getWritableDatabase();
int ret = 0;
try {
if (mNotes != null && mNotes.size() > 0) {
db.beginTransaction();//开始事务
try {
for (Note note : mNotes) {
ret += db.delete("db_note", "n_id=?", new String[]{note.getId() + ""});
}
db.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally {
db.endTransaction();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (db != null) {
db.close();
}
}
return ret;
}
}
package com.sendtion.xrichtextdemo.ui;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
/**
* 作者:Sendtion on 2016/10/21 0021 16:43
* 邮箱:sendtion@163.com
* 博客:http://sendtion.cn
* 描述:所有Activity基类
*/
public class BaseActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
}
/** 显示吐司 **/
public void showToast(String text) {
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
}
}
package com.sendtion.xrichtextdemo.ui;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import com.jcodecraeer.xrecyclerview.XRecyclerView;
import com.sendtion.xrichtextdemo.R;
import com.sendtion.xrichtextdemo.adapter.MyNoteListAdapter;
import com.sendtion.xrichtextdemo.bean.Note;
import com.sendtion.xrichtextdemo.db.NoteDao;
import com.sendtion.xrichtextdemo.view.SpacesItemDecoration;
import java.util.List;
/**
* 作者:Sendtion on 2016/10/21 0021 16:43
* 邮箱:sendtion@163.com
* 博客:http://sendtion.cn
* 描述:主界面
*/
public class MainActivity extends BaseActivity {
private static final String TAG = "MainActivity";
private XRecyclerView rv_list_main;
private MyNoteListAdapter mNoteListAdapter;
private List<Note> noteList;
private NoteDao noteDao;
private int groupId;//分类ID
private String groupName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_main);
setSupportActionBar(toolbar);
final FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab_main);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "新建笔记", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
Intent intent = new Intent(MainActivity.this, NewActivity.class);
intent.putExtra("groupName", groupName);
intent.putExtra("flag", 0);
startActivity(intent);
}
});
noteDao = new NoteDao(this);
rv_list_main = (XRecyclerView) findViewById(R.id.rv_list_main);
/****************** 设置XRecyclerView属性 **************************/
rv_list_main.addItemDecoration(new SpacesItemDecoration(0));//设置item间距
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);//竖向列表
rv_list_main.setLayoutManager(layoutManager);
rv_list_main.setLoadingMoreEnabled(false);//禁止上拉加载
rv_list_main.setPullRefreshEnabled(false);//禁止下拉刷新
//rv_list_question.setRefreshProgressStyle(ProgressStyle.BallSpinFadeLoader);
//rv_list_main.setLoadingMoreProgressStyle(ProgressStyle.BallRotate);
//rv_list_question.setArrowImageView(R.drawable.icon_arrow_downgrey);
/****************** 设置XRecyclerView属性 **************************/
mNoteListAdapter = new MyNoteListAdapter();
mNoteListAdapter.setmNotes(noteList);
rv_list_main.setAdapter(mNoteListAdapter);
rv_list_main.setLoadingListener(new MainActivity.MyLoadingListener());
mNoteListAdapter.setOnItemClickListener(new MyNoteListAdapter.OnRecyclerViewItemClickListener() {
@Override
public void onItemClick(View view, Note note) {
showToast(note.getTitle());
Intent intent = new Intent(MainActivity.this, NoteActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable("note", note);
intent.putExtra("data", bundle);
startActivity(intent);
}
});
mNoteListAdapter.setOnItemLongClickListener(new MyNoteListAdapter.OnRecyclerViewItemLongClickListener() {
@Override
public void onItemLongClick(View view, final Note note) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("提示");
builder.setMessage("确定删除笔记?");
builder.setCancelable(false);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int ret = noteDao.deleteNote(note.getId());
if (ret > 0){
showToast("删除成功");
refreshNoteList();
}
}
});
builder.setNegativeButton("取消", null);
builder.create().show();
}
});
}
/** 上拉加载和下拉刷新事件 **/
private class MyLoadingListener implements XRecyclerView.LoadingListener{
@Override
public void onRefresh() {//下拉刷新
}
@Override
public void onLoadMore() {//上拉加载
}
}
//刷新笔记列表
private void refreshNoteList(){
noteList = noteDao.queryNotesAll(groupId);
//Log.i(TAG, "###noteList: "+noteList);
mNoteListAdapter.setmNotes(noteList);
mNoteListAdapter.notifyDataSetChanged();
}
@Override
protected void onResume() {
super.onResume();
refreshNoteList();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_new_note:
Intent intent = new Intent(MainActivity.this, NewActivity.class);
intent.putExtra("groupName", groupName);
intent.putExtra("flag", 0);
startActivity(intent);
break;
}
return super.onOptionsItemSelected(item);
}
}
package com.sendtion.xrichtextdemo.ui;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.sendtion.xrichtext.RichTextEditor;
import com.sendtion.xrichtextdemo.R;
import com.sendtion.xrichtextdemo.bean.Group;
import com.sendtion.xrichtextdemo.bean.Note;
import com.sendtion.xrichtextdemo.db.GroupDao;
import com.sendtion.xrichtextdemo.db.NoteDao;
import com.sendtion.xrichtextdemo.util.CommonUtil;
import com.sendtion.xrichtextdemo.util.DateUtils;
import com.sendtion.xrichtextdemo.util.ImageUtils;
import com.sendtion.xrichtextdemo.util.SDCardUtil;
import com.sendtion.xrichtextdemo.util.ScreenUtils;
import com.sendtion.xrichtextdemo.util.StringUtils;
import java.util.Date;
import java.util.List;
public class NewActivity extends BaseActivity {
private EditText et_new_title;
private RichTextEditor et_new_content;
private TextView tv_new_time;
private TextView tv_new_group;
private GroupDao groupDao;
private NoteDao noteDao;
private Note note;//笔记对象
private String myTitle;
private String myContent;
private String myGroupName;
private String myNoteTime;
private int flag;//区分是新建笔记还是编辑笔记
private static final int cutTitleLength = 20;//截取的标题长度
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new);
initView();
}
private void initView() {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_new);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//toolbar.setNavigationIcon(R.drawable.ic_dialog_info);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dealwithExit();
}
});
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab_new);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
groupDao = new GroupDao(this);
noteDao = new NoteDao(this);
note = new Note();
et_new_title = (EditText) findViewById(R.id.et_new_title);
et_new_content = (RichTextEditor) findViewById(R.id.et_new_content);
tv_new_time = (TextView) findViewById(R.id.tv_new_time);
tv_new_group = (TextView) findViewById(R.id.tv_new_group);
Intent intent = getIntent();
flag = intent.getIntExtra("flag", 0);//0新建,1编辑
if (flag == 1){//编辑
Bundle bundle = intent.getBundleExtra("data");
note = (Note) bundle.getSerializable("note");
myTitle = note.getTitle();
myContent = note.getContent();
myNoteTime = note.getCreateTime();
Group group = groupDao.queryGroupById(note.getGroupId());
myGroupName = group.getName();
setTitle("编辑笔记");
tv_new_time.setText(note.getCreateTime());
tv_new_group.setText(myGroupName);
et_new_title.setText(note.getTitle());
et_new_content.post(new Runnable() {
@Override
public void run() {
showEditData(note.getContent());
}
});
} else {
setTitle("新建笔记");
if (myGroupName == null || "全部笔记".equals(myGroupName)) {
myGroupName = "默认笔记";
}
tv_new_group.setText(myGroupName);
myNoteTime = DateUtils.date2string(new Date());
tv_new_time.setText(myNoteTime);
}
}
/**
* 显示数据
*/
protected void showEditData(String content) {
et_new_content.clearAllLayout();
List<String> textList = StringUtils.cutStringByImgTag(content);
for (int i = 0; i < textList.size(); i++) {
String text = textList.get(i);
if (text.contains("<img")) {
String imagePath = StringUtils.getImgSrc(text);
int width = ScreenUtils.getScreenWidth(this);
int height = ScreenUtils.getScreenHeight(this);
et_new_content.measure(0,0);
Bitmap bitmap = ImageUtils.getSmallBitmap(imagePath, width, height);
//String imagePath = SDCardUtil.saveToSdCard(bitmap);
//Log.i("NewActivity", "###imagePath="+imagePath);
//et_content.insertImage(imagePath, et_content.getMeasuredWidth());
//editor.insertImage(imagePath, editor.getWidth());
//Bitmap bmp = et_content.getScaledBitmap(imagePath, et_content.getWidth());
if (bitmap != null){
et_new_content.addImageViewAtIndex(et_new_content.getLastIndex(), bitmap, imagePath);
} else {
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);
}
}
}
/**
* 负责处理编辑数据提交等事宜,请自行实现
*/
private String getEditData() {
List<RichTextEditor.EditData> editList = et_new_content.buildEditData();
StringBuffer content = new StringBuffer();
for (RichTextEditor.EditData itemData : editList) {
if (itemData.inputStr != null) {
content.append(itemData.inputStr);
//Log.d("RichEditor", "commit inputStr=" + itemData.inputStr);
} else if (itemData.imagePath != null) {
content.append("<img src=\"").append(itemData.imagePath).append("\"/>");
//Log.d("RichEditor", "commit imgePath=" + itemData.imagePath);
//imageList.add(itemData.imagePath);
}
}
return content.toString();
}
/**
* 保存数据,=0销毁当前界面,=1不销毁界面,为了防止在后台时保存笔记并销毁,应该只保存笔记
*/
private void saveNoteData(boolean isBackground) {
String noteTitle = et_new_title.getText().toString();
String noteContent = getEditData();
String groupName = tv_new_group.getText().toString();
String noteTime = tv_new_time.getText().toString();
Group group = groupDao.queryGroupByName(myGroupName);
if (group != null) {
if (noteTitle.length() == 0 ){//如果标题为空,则截取内容为标题
if (noteContent.length() > cutTitleLength){
noteTitle = noteContent.substring(0,cutTitleLength);
} else if (noteContent.length() > 0 && noteContent.length() <= cutTitleLength){
noteTitle = noteContent;
}
}
int groupId = group.getId();
note.setTitle(noteTitle);
note.setContent(noteContent);
note.setGroupId(groupId);
note.setGroupName(groupName);
note.setType(2);
note.setBgColor("#FFFFFF");
note.setIsEncrypt(0);
note.setCreateTime(DateUtils.date2string(new Date()));
if (flag == 0 ) {//新建笔记
if (noteTitle.length() == 0 && noteContent.length() == 0) {
if (!isBackground){
Toast.makeText(NewActivity.this, "请输入内容", Toast.LENGTH_SHORT).show();
}
} else {
long noteId = noteDao.insertNote(note);
//Log.i("", "noteId: "+noteId);
//查询新建笔记id,防止重复插入
note.setId((int) noteId);
flag = 1;//插入以后只能是编辑
if (!isBackground){
Intent intent = new Intent();
setResult(RESULT_OK, intent);
finish();
}
}
}else if (flag == 1) {//编辑笔记
if (!noteTitle.equals(myTitle) || !noteContent.equals(myContent)
|| !groupName.equals(myGroupName) || !noteTime.equals(myNoteTime)) {
noteDao.updateNote(note);
}
if (!isBackground){
finish();
}
}
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_new, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.action_insert_image:
callGallery();
break;
case R.id.action_new_save:
saveNoteData(false);
break;
}
return super.onOptionsItemSelected(item);
}
/**
* 调用图库选择
*/
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/*");// 相片类型
startActivityForResult(intent, 1);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == 1) {
if (data != null) {
et_new_content.measure(0,0);
String imagePath = SDCardUtil.getFilePathByUri(this, data.getData());
//Log.i("NewActivity", "###imagePath="+imagePath);
et_new_content.insertImage(imagePath, et_new_content.getMeasuredWidth());
}
}
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onStop() {
super.onStop();
//如果APP处于后台,或者手机锁屏,则启用密码锁
if (CommonUtil.isAppOnBackground(getApplicationContext()) ||
CommonUtil.isLockScreeen(getApplicationContext())){
saveNoteData(true);//处于后台时保存数据
}
}
/**
* 退出处理
*/
private void dealwithExit(){
String noteTitle = et_new_title.getText().toString();
String noteContent = getEditData();
String groupName = tv_new_group.getText().toString();
String noteTime = tv_new_time.getText().toString();
if (flag == 0) {//新建笔记
if (noteTitle.length() > 0 || noteContent.length() > 0) {
saveNoteData(false);
}
}else if (flag == 1) {//编辑笔记
if (!noteTitle.equals(myTitle) || !noteContent.equals(myContent)
|| !groupName.equals(myGroupName) || !noteTime.equals(myNoteTime)) {
saveNoteData(false);
}
}
finish();
}
@Override
public void onBackPressed() {
dealwithExit();
}
}
package com.sendtion.xrichtextdemo.ui;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import com.sendtion.xrichtext.RichTextView;
import com.sendtion.xrichtextdemo.R;
import com.sendtion.xrichtextdemo.bean.Group;
import com.sendtion.xrichtextdemo.bean.Note;
import com.sendtion.xrichtextdemo.db.GroupDao;
import com.sendtion.xrichtextdemo.db.NoteDao;
import com.sendtion.xrichtextdemo.util.CommonUtil;
import com.sendtion.xrichtextdemo.util.StringUtils;
import java.util.List;
public class NoteActivity extends BaseActivity {
private TextView tv_note_title;//笔记标题
private RichTextView tv_note_content;//笔记内容
private TextView tv_note_time;//笔记创建时间
private TextView tv_note_group;//选择笔记分类
//private ScrollView scroll_view;
private Note note;//笔记对象
private String myTitle;
private String myContent;
private String myGroupName;
private NoteDao noteDao;
private GroupDao groupDao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_note);
initView();
}
private void initView() {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_note);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//toolbar.setNavigationIcon(R.drawable.ic_dialog_info);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab_note);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
noteDao = new NoteDao(this);
groupDao = new GroupDao(this);
tv_note_title = (TextView) findViewById(R.id.tv_note_title);//标题
tv_note_title.setTextIsSelectable(true);
tv_note_content = (RichTextView) findViewById(R.id.tv_note_content);//内容
tv_note_time = (TextView) findViewById(R.id.tv_note_time);
tv_note_group = (TextView) findViewById(R.id.tv_note_group);
Intent intent = getIntent();
Bundle bundle = intent.getBundleExtra("data");
note = (Note) bundle.getSerializable("note");
myTitle = note.getTitle();
myContent = note.getContent();
Group group = groupDao.queryGroupById(note.getGroupId());
myGroupName = group.getName();
tv_note_title.setText(myTitle);
tv_note_content.post(new Runnable() {
@Override
public void run() {
showEditData(myContent);
}
});
tv_note_time.setText(note.getCreateTime());
tv_note_group.setText(myGroupName);
setTitle("笔记详情");
}
/**
* 显示数据
* @param html
*/
private void showEditData(String html) {
tv_note_content.clearAllLayout();
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);
Bitmap bmp = tv_note_content.getScaledBitmap(imagePath, tv_note_content.getWidth());
if (bmp != null){
tv_note_content.addImageViewAtIndex(tv_note_content.getLastIndex(), bmp, imagePath);
} else {
tv_note_content.addTextViewAtIndex(tv_note_content.getLastIndex(), text);
}
} else {
tv_note_content.addTextViewAtIndex(tv_note_content.getLastIndex(), text);
}
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_note, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.action_note_edit://编辑笔记
Intent intent = new Intent(NoteActivity.this, NewActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable("note", note);
intent.putExtra("data", bundle);
intent.putExtra("flag", 1);//编辑笔记
startActivity(intent);
finish();
break;
case R.id.action_note_share://分享笔记
CommonUtil.shareTextAndImage(this, note.getTitle(), note.getContent(), null);//分享图文
break;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onStop() {
super.onStop();
}
@Override
public void onBackPressed() {
finish();
}
}
package com.sendtion.xrichtextdemo.util;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.PowerManager;
import android.util.Log;
import java.io.File;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by sendtion on 2016/3/30.
*/
public class CommonUtil {
/**
* 判断应用是否处于后台
* @param context
* @return
*/
public static boolean isAppOnBackground(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(context.getPackageName())) {
return true;
}
}
return false;
}
/**
* 判断是否锁屏
* @param context
* @return
*/
public static boolean isLockScreeen(Context context){
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
boolean isScreenOn = pm.isScreenOn();//如果为true,则表示屏幕“亮”了,否则屏幕“暗”了。
if (isScreenOn){
return false;
} else {
return true;
}
}
/**
* 判断是否为手机号
*/
public static boolean isPhone(String inputText) {
Pattern p = Pattern.compile("^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$");
Matcher m = p.matcher(inputText);
return m.matches();
}
/**
* 判断格式是否为email
*/
public static boolean isEmail(String email) {
String str = "^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]" +
"{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$";
Pattern p = Pattern.compile(str);
Matcher m = p.matcher(email);
return m.matches();
}
/**
* 分享文字笔记
*/
public static void shareText(Context context, String content){
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_TEXT, content);
shareIntent.setType("text/plain");
//设置分享列表的标题,并且每次都显示分享列表
context.startActivity(Intent.createChooser(shareIntent, "分享到"));
}
/**
* 分享单张图片
* @param context
* @param imagePath
*/
public static void shareImage(Context context, String imagePath) {
//String imagePath = Environment.getExternalStorageDirectory() + File.separator + "test.jpg";
Uri imageUri = Uri.fromFile(new File(imagePath));//由文件得到uri
Log.d("share", "uri:" + imageUri); //输出:file:///storage/emulated/0/test.jpg
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri);
shareIntent.setType("image/*");
context.startActivity(Intent.createChooser(shareIntent, "分享到"));
}
/**
* 分享功能
*
* @param context
* 上下文
* @param msgTitle
* 消息标题
* @param msgText
* 消息内容
* @param imgPath
* 图片路径,不分享图片则传null
*/
public static void shareTextAndImage(Context context, String msgTitle, String msgText, String imgPath) {
Intent intent = new Intent(Intent.ACTION_SEND);
if (imgPath == null || imgPath.equals("")) {
intent.setType("text/plain"); // 纯文本
} else {
File f = new File(imgPath);
if (f != null && f.exists() && f.isFile()) {
intent.setType("image/jpg");
Uri u = Uri.fromFile(f);
intent.putExtra(Intent.EXTRA_STREAM, u);
}
}
intent.putExtra(Intent.EXTRA_SUBJECT, msgTitle);
intent.putExtra(Intent.EXTRA_TEXT, msgText);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(Intent.createChooser(intent, "分享到"));
}
}
package com.sendtion.xrichtextdemo.util;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* 时间处理
*/
public class DateUtils {
public static final String ENG_DATE_FROMAT = "EEE, d MMM yyyy HH:mm:ss z";
public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
public static final String YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
public static final String YYYY_MM_DD = "yyyy-MM-dd";
public static final String YYYY = "yyyy";
public static final String MM = "MM";
public static final String DD = "dd";
public static String generateTime(long time) {
int totalSeconds = (int) (time / 1000);
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
int hours = totalSeconds / 3600;
return hours > 0 ? String.format("%02d:%02d:%02d", hours, minutes, seconds) : String.format("%02d:%02d", minutes, seconds);
}
/** 根据long毫秒数,获得时分秒 **/
public static String getDateFormatByLong(long time) {
int totalSeconds = (int) (time / 1000);
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
int hours = totalSeconds / 3600;
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
}
/**
* @param
* @return
* @作者
* @创建日期
* @创建时间
* @描述 —— 格式化日期对象
*/
public static Date date2date(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS);
String str = sdf.format(date);
try {
date = sdf.parse(str);
} catch (Exception e) {
return null;
}
return date;
}
/**
* @param
* @return
* @作者
* @创建日期
* @创建时间
* @描述 —— 时间对象转换成字符串
*/
public static String date2string(Date date) {
String strDate = "";
SimpleDateFormat sdf = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS);
strDate = sdf.format(date);
return strDate;
}
/**
* 通过时间获得文件名
* @param date
* @return
*/
public static String getFileNameByDate(Date date) {
String strDate = "";
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
strDate = sdf.format(date);
return strDate;
}
/**
* @param
* @return
* @作者
* @创建日期
* @创建时间
* @描述 —— sql时间对象转换成字符串
*/
public static String timestamp2string(Timestamp timestamp) {
String strDate = "";
SimpleDateFormat sdf = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS);
strDate = sdf.format(timestamp);
return strDate;
}
/**
* @param dateString
* @return
* @作者 王建明
* @创建日期 2012-7-13
* @创建时间
* @描述 —— 字符串转换成时间对象
*/
public static Date string2date(String dateString) {
Date formateDate = null;
DateFormat format = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS);
try {
formateDate = format.parse(dateString);
} catch (ParseException e) {
return null;
}
return formateDate;
}
/**
* @param date
* @return
* @作者
* @创建日期
* @创建时间
* @描述 —— Date类型转换为Timestamp类型
*/
public static Timestamp date2timestamp(Date date) {
if (date == null)
return null;
return new Timestamp(date.getTime());
}
/**
* @return
* @作者
* @创建日期
* @创建时间
* @描述 —— 获得当前年份
*/
public static String getNowYear() {
SimpleDateFormat sdf = new SimpleDateFormat(YYYY);
return sdf.format(new Date());
}
/**
* @return
* @作者
* @创建日期
* @创建时间
* @描述 —— 获得当前月份
*/
public static String getNowMonth() {
SimpleDateFormat sdf = new SimpleDateFormat(MM);
return sdf.format(new Date());
}
/**
* @return
* @作者
* @创建日期
* @创建时间
* @描述 —— 获得当前日期中的日
*/
public static String getNowDay() {
SimpleDateFormat sdf = new SimpleDateFormat(DD);
return sdf.format(new Date());
}
/**
* @param time
* @return
* @作者
* @创建日期
* @创建时间
* @描述 —— 指定时间距离当前时间的中文信息
*/
public static String getFriendlyTime(long time) {
Calendar cal = Calendar.getInstance();
long timel = cal.getTimeInMillis() - time;
if (timel / 1000 < 60) {
return "1分钟以内";
} else if (timel / 1000 / 60 < 60) {
return timel / 1000 / 60 + "分钟前";
} else if (timel / 1000 / 60 / 60 < 24) {
return timel / 1000 / 60 / 60 + "小时前";
} else {
return timel / 1000 / 60 / 60 / 24 + "天前";
}
}
/**
* 以友好的方式显示时间
* @param time
* @return
*/
public static String getFriendlyTime(Date time) {
//获取time距离当前的秒数
int ct = (int)((System.currentTimeMillis() - time.getTime())/1000);
if(ct == 0) {
return "刚刚";
}
if(ct > 0 && ct < 60) {
return ct + "秒前";
}
if(ct >= 60 && ct < 3600) {
return Math.max(ct / 60,1) + "分钟前";
}
if(ct >= 3600 && ct < 86400)
return ct / 3600 + "小时前";
if(ct >= 86400 && ct < 2592000){ //86400 * 30
int day = ct / 86400 ;
return day + "天前";
}
if(ct >= 2592000 && ct < 31104000) { //86400 * 30
return ct / 2592000 + "月前";
}
return ct / 31104000 + "年前";
}
/**
* 格式化日期字符串
* @param currentTime
* @return
*/
public static String formatString(String currentTime) {
DateFormat format = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS);
return format.format(currentTime);
}
}
package com.sendtion.xrichtextdemo.util;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri;
import android.util.Base64;
import android.util.Log;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
/**
* Created by sendtion on 2016/5/25.
*/
public class ImageUtils {
/**
* 图片压缩处理,size参数为压缩比,比如size为2,则压缩为1/4
**/
public static Bitmap compressBitmap(String path, byte[] data, Context context, Uri uri, int size, boolean width) {
BitmapFactory.Options options = null;
if (size > 0) {
BitmapFactory.Options info = new BitmapFactory.Options();
/**如果设置true的时候,decode时候Bitmap返回的为数据将空*/
info.inJustDecodeBounds = false;
decodeBitmap(path, data, context, uri, info);
int dim = info.outWidth;
if (!width) dim = Math.max(dim, info.outHeight);
options = new BitmapFactory.Options();
/**把图片宽高读取放在Options里*/
options.inSampleSize = size;
}
Bitmap bm = null;
try {
bm = decodeBitmap(path, data, context, uri, options);
} catch (Exception e) {
e.printStackTrace();
}
return bm;
}
/**
* 把byte数据解析成图片
*/
private static Bitmap decodeBitmap(String path, byte[] data, Context context, Uri uri, BitmapFactory.Options options) {
Bitmap result = null;
if (path != null) {
result = BitmapFactory.decodeFile(path, options);
} else if (data != null) {
result = BitmapFactory.decodeByteArray(data, 0, data.length, options);
} else if (uri != null) {
ContentResolver cr = context.getContentResolver();
InputStream inputStream = null;
try {
inputStream = cr.openInputStream(uri);
result = BitmapFactory.decodeStream(inputStream, null, options);
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
/**
* 把bitmap转换成String
*
* @param filePath
* @return
*/
public static String bitmapToString(String filePath) {
Bitmap bm = getSmallBitmap(filePath, 480, 800);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 40, baos);
byte[] b = baos.toByteArray();
return Base64.encodeToString(b, Base64.DEFAULT);
}
/**
* 计算图片的缩放值
*
* @param options
* @param reqWidth
* @param reqHeight
* @return
*/
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and
// width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will
// guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
/**
* 根据路径获得突破并压缩返回bitmap用于显示
*
* @return
*/
public static Bitmap getSmallBitmap(String filePath, int newWidth, int newHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, newWidth, newHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
Bitmap newBitmap = compressImage(bitmap, 300);
if (bitmap != null){
bitmap.recycle();
}
return newBitmap;
}
/**
* 根据view的宽度,动态缩放bitmap尺寸
*
* @param width
* view的宽度
*/
// public Bitmap getScaledBitmap(String filePath, int width) {
// BitmapFactory.Options options = new BitmapFactory.Options();
// options.inJustDecodeBounds = true;
// BitmapFactory.decodeFile(filePath, options);
// int sampleSize = options.outWidth > width ? options.outWidth / width
// + 1 : 1;
// options.inJustDecodeBounds = false;
// options.inSampleSize = sampleSize;
// return BitmapFactory.decodeFile(filePath, options);
// }
/**
* 对图片进行按比例设置
* @param bitmap 要处理的图片
* @return 返回处理好的图片
*/
// public static Bitmap getScaleBitmap(Bitmap bitmap, float widthScale, float heightScale){
// Matrix matrix = new Matrix();
// matrix.postScale(widthScale, heightScale);
// if(bitmap == null){
// return null;
// }
// Bitmap resizeBmp =
// Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
// return resizeBmp;
// }
/**
* 根据路径删除图片
*
* @param path
*/
public static void deleteTempFile(String path) {
File file = new File(path);
if (file.exists()) {
file.delete();
}
}
/**
* 添加到图库
*/
public static void galleryAddPic(Context context, String path) {
Intent mediaScanIntent = new Intent(
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(path);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
context.sendBroadcast(mediaScanIntent);
}
//使用Bitmap加Matrix来缩放
public static Bitmap resizeImage(Bitmap bitmapOrg, int newWidth, int newHeight)
{
// Bitmap bitmapOrg = BitmapFactory.decodeFile(imagePath);
// 获取这个图片的宽和高
int width = bitmapOrg.getWidth();
int height = bitmapOrg.getHeight();
//如果宽度为0 保持原图
if(newWidth == 0){
newWidth = width;
newHeight = height;
}
// 创建操作图片用的matrix对象
Matrix matrix = new Matrix();
// 计算宽高缩放率
float scaleWidth = newWidth / width;
float scaleHeight = newHeight / height;
// 缩放图片动作
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0, newWidth,
newHeight, matrix, true);
//Log.e("###newWidth=", resizedBitmap.getWidth()+"");
//Log.e("###newHeight=", resizedBitmap.getHeight()+"");
resizedBitmap = compressImage(resizedBitmap, 100);//质量压缩
return resizedBitmap;
}
//使用BitmapFactory.Options的inSampleSize参数来缩放
public static Bitmap resizeImage2(String path, int width,int height)
{
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;//不加载bitmap到内存中
BitmapFactory.decodeFile(path,options);
int outWidth = options.outWidth;
int outHeight = options.outHeight;
options.inDither = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inSampleSize = 1;
if (outWidth != 0 && outHeight != 0 && width != 0 && height != 0)
{
int sampleSize=(outWidth/width+outHeight/height)/2;
Log.d("###", "sampleSize = " + sampleSize);
options.inSampleSize = sampleSize;
}
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path, options);
}
/**
* 通过像素压缩图片,将修改图片宽高,适合获得缩略图,Used to get thumbnail
* @param srcPath
* @return
*/
public static Bitmap compressBitmapByPath(String srcPath, float pixelW, float pixelH) {
BitmapFactory.Options newOpts = new BitmapFactory.Options();
//开始读入图片,此时把options.inJustDecodeBounds 设回true了
newOpts.inJustDecodeBounds = true;
newOpts.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeFile(srcPath,newOpts);//此时返回bm为空
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
//现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
float hh = pixelH;//这里设置高度为800f
float ww = pixelW;//这里设置宽度为480f
//缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
int be = 1;//be=1表示不缩放
if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放
be = (int) (newOpts.outWidth / ww);
} else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放
be = (int) (newOpts.outHeight / hh);
}
if (be <= 0)
be = 1;
newOpts.inSampleSize = be;//设置缩放比例
//重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
// return compress(bitmap, maxSize); // 这里再进行质量压缩的意义不大,反而耗资源,删除
return bitmap;
}
/**
* 通过大小压缩,将修改图片宽高,适合获得缩略图,Used to get thumbnail
* @param image
* @param pixelW
* @param pixelH
* @return
*/
public static Bitmap compressBitmapByBmp(Bitmap image, float pixelW, float pixelH) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, os);
if( os.toByteArray().length / 1024>1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出
os.reset();//重置baos即清空baos
image.compress(Bitmap.CompressFormat.JPEG, 50, os);//这里压缩50%,把压缩后的数据存放到baos中
}
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
BitmapFactory.Options newOpts = new BitmapFactory.Options();
//开始读入图片,此时把options.inJustDecodeBounds 设回true了
newOpts.inJustDecodeBounds = true;
newOpts.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeStream(is, null, newOpts);
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
float hh = pixelH;// 设置高度为240f时,可以明显看到图片缩小了
float ww = pixelW;// 设置宽度为120f,可以明显看到图片缩小了
//缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
int be = 1;//be=1表示不缩放
if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放
be = (int) (newOpts.outWidth / ww);
} else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放
be = (int) (newOpts.outHeight / hh);
}
if (be <= 0) be = 1;
newOpts.inSampleSize = be;//设置缩放比例
//重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
is = new ByteArrayInputStream(os.toByteArray());
bitmap = BitmapFactory.decodeStream(is, null, newOpts);
int desWidth = (int) (w / be);
int desHeight = (int) (h / be);
bitmap = Bitmap.createScaledBitmap(bitmap, desWidth, desHeight, true);
//压缩好比例大小后再进行质量压缩
// return compress(bitmap, maxSize); // 这里再进行质量压缩的意义不大,反而耗资源,删除
return bitmap;
}
/**
* 质量压缩
* @param image
* @param maxSize
*/
public static Bitmap compressImage(Bitmap image, int maxSize){
ByteArrayOutputStream os = new ByteArrayOutputStream();
// scale
int options = 80;
// Store the bitmap into output stream(no compress)
image.compress(Bitmap.CompressFormat.JPEG, options, os);
// Compress by loop
while ( os.toByteArray().length / 1024 > maxSize) {
// Clean up os
os.reset();
// interval 10
options -= 10;
image.compress(Bitmap.CompressFormat.JPEG, options, os);
}
Bitmap bitmap = null;
byte[] b = os.toByteArray();
if (b.length != 0) {
bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
}
return bitmap;
}
/**
* 对图片进行缩放
* @param bgimage
* @param newWidth
* @param newHeight
* @return
*/
public static Bitmap zoomImage(Bitmap bgimage, double newWidth, double newHeight) {
// //使用方式
// Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.default_img);
// int paddingLeft = getPaddingLeft();
// int paddingRight = getPaddingRight();
// int bmWidth = bitmap.getWidth();//图片高度
// int bmHeight = bitmap.getHeight();//图片宽度
// int zoomWidth = getWidth() - (paddingLeft + paddingRight);
// int zoomHeight = (int) (((float)zoomWidth / (float)bmWidth) * bmHeight);
// Bitmap newBitmap = zoomImage(bitmap, zoomWidth,zoomHeight);
// 获取这个图片的宽和高
float width = bgimage.getWidth();
float height = bgimage.getHeight();
//如果宽度为0 保持原图
if(newWidth == 0){
newWidth = width;
newHeight = height;
}
// 创建操作图片用的matrix对象
Matrix matrix = new Matrix();
// 计算宽高缩放率
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// 缩放图片动作
matrix.postScale(scaleWidth, scaleHeight);
Bitmap bitmap = Bitmap.createBitmap(bgimage, 0, 0, (int) width,
(int) height, matrix, true);
bitmap = compressImage(bitmap, 100);//质量压缩
return bitmap;
}
}
package com.sendtion.xrichtextdemo.util;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.format.Time;
import android.widget.Toast;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class SDCardUtil {
public static String SDCardRoot = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;
/**
* 检查是否存在SDCard
* @return
*/
public static boolean hasSdcard(){
String state = Environment.getExternalStorageState();
if(state.equals(Environment.MEDIA_MOUNTED)){
return true;
}else{
return false;
}
}
/**
* 获得文章图片保存路径
* @return
*/
public static String getPictureDir(){
String imageCacheUrl = SDCardRoot + "TimeNote" + File.separator + "Picture" + File.separator ;
File file = new File(imageCacheUrl);
if(!file.exists())
file.mkdir(); //如果不存在则创建
return imageCacheUrl;
}
/**
* 获得图片缓存路径
* @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
* @return
*/
public static String saveToSdCard(Bitmap bitmap) {
String imageUrl = getPictureDir() + getNoteImageName();
File file = new File(imageUrl);
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();
}
return file.getAbsolutePath();
}
/**
* 保存到指定路径,笔记中插入图片
* @param bitmap
* @param path
* @return
*/
public static String saveToSdCard(Bitmap bitmap, String path) {
//String fileUrl = getImageCacheDir() + File.separator + "head" + DateUtil.getTime2FileName() + ".jpg";
File file = new File(path);
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();
}
//System.out.println("文件保存路径:"+ file.getAbsolutePath());
return file.getAbsolutePath();
}
/**
* 根据Uri获取图片文件的绝对路径
*/
public static String getFilePathByUri(Context context, final Uri uri) {
if (null == uri) {
return null;
}
final String scheme = uri.getScheme();
String data = null;
if (scheme == null) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
Cursor cursor = context.getContentResolver().query(uri,
new String[] { MediaStore.Images.ImageColumns.DATA }, null, null, null);
if (null != cursor) {
if (cursor.moveToFirst()) {
int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
if (index > -1) {
data = cursor.getString(index);
}
}
cursor.close();
}
}
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;
}
}
package com.sendtion.xrichtextdemo.util;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.WindowManager;
/**
* Created by sendtion on 2016/5/25.
*/
public class ScreenUtils {
/**
* 获得屏幕宽度
* @param context
* @return
*/
public static int getScreenWidth(Context context)
{
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return outMetrics.widthPixels;
}
/**
* 获得屏幕高度
* @param context
* @return
*/
public static int getScreenHeight(Context context) {
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return outMetrics.heightPixels;
}
/**
* 获得状态栏高度
* @param context
* @return
*/
public static int getStatusHeight(Context context) {
int statusHeight = -1;
try {
Class<?> clazz = Class.forName("com.android.internal.R$dimen");
Object object = clazz.newInstance();
int height = Integer.parseInt(clazz.getField("status_bar_height")
.get(object).toString());
statusHeight = context.getResources().getDimensionPixelSize(height);
} catch (Exception e) {
e.printStackTrace();
}
return statusHeight;
}
/**
* 获取当前屏幕截图,包含状态栏
*/
public static Bitmap snapShotWithStatusBar(Activity activity){
View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bmp = view.getDrawingCache();
int width = getScreenWidth(activity);
int height = getScreenHeight(activity);
Bitmap bp = null;
bp = Bitmap.createBitmap(bmp, 0, 0, width, height);
view.destroyDrawingCache();
return bp;
}
/**
* 获取当前屏幕截图,不包含状态栏
*
*/
public static Bitmap snapShotWithoutStatusBar(Activity activity){
View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bmp = view.getDrawingCache();
Rect frame = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
int statusBarHeight = frame.top;
int width = getScreenWidth(activity);
int height = getScreenHeight(activity);
Bitmap bp = null;
bp = Bitmap.createBitmap(bmp, 0, statusBarHeight, width, height
- statusBarHeight);
view.destroyDrawingCache();
return bp;
}
}
package com.sendtion.xrichtextdemo.util;
import android.graphics.Color;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.CharacterStyle;
import android.text.style.ForegroundColorSpan;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by sendtion on 2016/6/24.
*/
public class StringUtils {
/**
* @param targetStr 要处理的字符串
* @description 切割字符串,将文本和img标签碎片化,如"ab<img>cd"转换为"ab"、"<img>"、"cd"
*/
public static List<String> cutStringByImgTag(String targetStr) {
List<String> splitTextList = new ArrayList<String>();
Pattern pattern = Pattern.compile("<img.*?src=\\\"(.*?)\\\".*?>");
Matcher matcher = pattern.matcher(targetStr);
int lastIndex = 0;
while (matcher.find()) {
if (matcher.start() > lastIndex) {
splitTextList.add(targetStr.substring(lastIndex, matcher.start()));
}
splitTextList.add(targetStr.substring(matcher.start(), matcher.end()));
lastIndex = matcher.end();
}
if (lastIndex != targetStr.length()) {
splitTextList.add(targetStr.substring(lastIndex, targetStr.length()));
}
return splitTextList;
}
/**
* 获取img标签中的src值
* @param content
* @return
*/
public static String getImgSrc(String content){
String str_src = null;
//目前img标签标示有3种表达式
//<img alt="" src="1.jpg"/> <img alt="" src="1.jpg"></img> <img alt="" src="1.jpg">
//开始匹配content中的<img />标签
Pattern p_img = Pattern.compile("<(img|IMG)(.*?)(/>|></img>|>)");
Matcher m_img = p_img.matcher(content);
boolean result_img = m_img.find();
if (result_img) {
while (result_img) {
//获取到匹配的<img />标签中的内容
String str_img = m_img.group(2);
//开始匹配<img />标签中的src
Pattern p_src = Pattern.compile("(src|SRC)=(\"|\')(.*?)(\"|\')");
Matcher m_src = p_src.matcher(str_img);
if (m_src.find()) {
str_src = m_src.group(3);
}
//结束匹配<img />标签中的src
//匹配content中是否存在下一个<img />标签,有则继续以上步骤匹配<img />标签中的src
result_img = m_img.find();
}
}
return str_src;
}
/**
* 关键字高亮显示
* @param target 需要高亮的关键字
* @param text 需要显示的文字
* @return spannable 处理完后的结果,记得不要toString(),否则没有效果
* SpannableStringBuilder textString = TextUtilTools.highlight(item.getItemName(), KnowledgeActivity.searchKey);
* vHolder.tv_itemName_search.setText(textString);
*/
public static SpannableStringBuilder highlight(String text, String target) {
SpannableStringBuilder spannable = new SpannableStringBuilder(text);
CharacterStyle span = null;
Pattern p = Pattern.compile(target);
Matcher m = p.matcher(text);
while (m.find()) {
span = new ForegroundColorSpan(Color.parseColor("#EE5C42"));// 需要重复!
spannable.setSpan(span, m.start(), m.end(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return spannable;
}
}
package com.sendtion.xrichtextdemo.view;
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* Created by Sendtion on 2016/7/4.
* RecyclerView Item的间距
*/
public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
private int space;
public SpacesItemDecoration(int space) {
this.space = space;
}
@Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
outRect.left = space;
outRect.right = space;
outRect.bottom = space;
// Add top margin only for the first item to avoid double space between items
if(parent.getChildPosition(view) == 0)
outRect.top = space;
}
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.sendtion.xrichtextdemo.ui.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar_main"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:visibility="gone"
app:srcCompat="@android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.sendtion.xrichtextdemo.ui.NewActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar_new"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_new" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_new"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:visibility="gone"
app:srcCompat="@android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.sendtion.xrichtextdemo.ui.NoteActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar_note"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_note" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_note"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:visibility="gone"
app:srcCompat="@android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/grey_100"
android:paddingTop="@dimen/activity_margin_5"
android:paddingBottom="@dimen/activity_margin_5"
android:paddingLeft="@dimen/activity_margin_10"
android:paddingRight="@dimen/activity_margin_10"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.sendtion.xrichtextdemo.ui.MainActivity"
tools:showIn="@layout/activity_main">
<com.jcodecraeer.xrecyclerview.XRecyclerView
android:id="@+id/rv_list_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content_new"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/grey_100"
android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.sendtion.xrichtextdemo.ui.NewActivity"
tools:showIn="@layout/activity_new">
<EditText
android:id="@+id/et_new_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:hint="请输入标题"
android:textSize="@dimen/text_size_18"
android:textColor="@color/grey_700"
android:padding="@dimen/activity_margin_16"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/activity_margin_16"
android:paddingRight="@dimen/activity_margin_16"
android:paddingBottom="@dimen/activity_margin_16">
<TextView
android:id="@+id/tv_new_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/text_size_14"
android:textColor="@color/grey_500"
android:layout_alignParentLeft="true"/>
<TextView
android:id="@+id/tv_new_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/text_size_14"
android:textColor="@color/grey_500"
android:layout_alignParentRight="true"/>
</RelativeLayout>
<com.sendtion.xrichtext.RichTextEditor
android:id="@+id/et_new_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="@dimen/text_size_16"
android:textColor="@color/grey_600"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content_note"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/grey_100"
android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.sendtion.xrichtextdemo.ui.NoteActivity"
tools:showIn="@layout/activity_note">
<TextView
android:id="@+id/tv_note_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:textSize="@dimen/text_size_18"
android:textColor="@color/grey_700"
android:padding="@dimen/activity_margin_16"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/activity_margin_16"
android:paddingRight="@dimen/activity_margin_16"
android:paddingBottom="@dimen/activity_margin_16">
<TextView
android:id="@+id/tv_note_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/text_size_14"
android:textColor="@color/grey_500"
android:layout_alignParentLeft="true"/>
<TextView
android:id="@+id/tv_note_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/text_size_14"
android:textColor="@color/grey_500"
android:layout_alignParentRight="true"/>
</RelativeLayout>
<com.sendtion.xrichtext.RichTextView
android:id="@+id/tv_note_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="@dimen/text_size_16"
android:textColor="@color/grey_600"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view_note"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardUseCompatPadding="true"
app:cardPreventCornerOverlap="true"
app:cardCornerRadius="3dp"
app:cardElevation="1dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/activity_margin_10"
android:background="?android:attr/selectableItemBackground">
<TextView
android:id="@+id/tv_list_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="标题"
android:textColor="@color/grey_700"
android:textSize="@dimen/text_size_18" />
<TextView
android:id="@+id/tv_list_summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_below="@+id/tv_list_title"
android:maxLines="2"
android:text="笔记摘要"
android:textColor="@color/grey_500"
android:textSize="@dimen/text_size_14" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
android:layout_below="@+id/tv_list_summary">
<TextView
android:id="@+id/tv_list_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:text="时间"
android:textColor="@color/grey_400"
android:textSize="@dimen/text_size_14" />
<TextView
android:id="@+id/tv_list_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="分类"
android:textColor="@color/grey_400"
android:textSize="@dimen/text_size_14" />
</RelativeLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>
\ No newline at end of file
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.sendtion.xrichtextdemo.ui.MainActivity">
<item
android:id="@+id/action_new_note"
android:orderInCategory="100"
android:title="新建"
app:showAsAction="always" />
</menu>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.sendtion.xrichtextdemo.ui.NewActivity">
<item
android:id="@+id/action_insert_image"
android:orderInCategory="100"
android:title="图片"
app:showAsAction="always" />
<item
android:id="@+id/action_new_save"
android:orderInCategory="100"
android:title="保存"
app:showAsAction="always" />
</menu>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.sendtion.xrichtextdemo.ui.NoteActivity">
<item
android:id="@+id/action_note_share"
android:orderInCategory="100"
android:title="分享"
app:showAsAction="always" />
<item
android:id="@+id/action_note_edit"
android:orderInCategory="100"
android:title="编辑"
app:showAsAction="always" />
</menu>
\ No newline at end of file
<resources>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<!-- Grey-->
<color name="grey_000">#FFFFFF</color>
<color name="grey_050">#FAFAFA</color>
<color name="grey_100">#F5F5F5</color>
<color name="grey_200">#EEEEEE</color>
<color name="grey_300">#E0E0E0</color>
<color name="grey_400">#BDBDBD</color>
<color name="grey_500">#9E9E9E</color>
<color name="grey_600">#757575</color>
<color name="grey_700">#616161</color>
<color name="grey_800">#424242</color>
<color name="grey_900">#212121</color>
</resources>
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="activity_margin_5">5dp</dimen>
<dimen name="activity_margin_10">10dp</dimen>
<dimen name="activity_margin_16">16dp</dimen>
<dimen name="text_size_14">14dp</dimen>
<dimen name="text_size_16">16dp</dimen>
<dimen name="text_size_18">18dp</dimen>
<dimen name="text_size_20">20dp</dimen>
</resources>
<resources>
<string name="app_name">XRichText</string>
<string name="action_settings">Settings</string>
<string name="title_activity_new">NewActivity</string>
<string name="title_activity_note">NoteActivity</string>
</resources>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>
package com.sendtion.xrichtextdemo;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
\ No newline at end of file
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()//jcenter仓库
mavenCentral()//mavenCentral仓库
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6'
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4.0.0"
//classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
classpath 'com.github.dcendents:android-maven-gradle-plugin:latest.release'
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Mon Dec 28 10:00:20 PST 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
include ':app', ':xrichtext'
apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'com.jfrog.bintray'
android {
compileSdkVersion 24
buildToolsVersion "24.0.0"
defaultConfig {
minSdkVersion 14
targetSdkVersion 24
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:24.2.1'
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 {
repositories.mavenInstaller {
// 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
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:\Android\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
package com.sendtion.xrichtext;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.sendtion.xrichtext.test", appContext.getPackageName());
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sendtion.xrichtext">
<application android:allowBackup="true" android:label="@string/app_name"
android:supportsRtl="true">
</application>
</manifest>
package com.sendtion.xrichtext;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.AttributeSet;
import android.widget.ImageView;
/**
* 这只是一个简单的ImageView,可以存放Bitmap和Path等信息
*
* @author xmuSistone
*/
public class DataImageView extends ImageView {
private String absolutePath;
private Bitmap bitmap;
public DataImageView(Context context) {
this(context, null);
}
public DataImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DataImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public String getAbsolutePath() {
return absolutePath;
}
public void setAbsolutePath(String absolutePath) {
this.absolutePath = absolutePath;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
}
package com.sendtion.xrichtext;
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;
import android.widget.EditText;
/**
* 这个是从stackOverFlow上面找到的解决方案,主要用途是处理软键盘回删按钮backSpace时回调OnKeyListener
*
* @author xmuSistone
*/
public class DeletableEditText extends EditText {
public DeletableEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public DeletableEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DeletableEditText(Context context) {
super(context);
}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return new DeleteInputConnection(super.onCreateInputConnection(outAttrs),
true);
}
private class DeleteInputConnection extends InputConnectionWrapper {
public DeleteInputConnection(InputConnection target, boolean mutable) {
super(target, mutable);
}
@Override
public boolean sendKeyEvent(KeyEvent event) {
return super.sendKeyEvent(event);
}
@Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
if (beforeLength == 1 && afterLength == 0) {
return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_DEL))
&& sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
KeyEvent.KEYCODE_DEL));
}
return super.deleteSurroundingText(beforeLength, afterLength);
}
}
}
\ No newline at end of file
package com.sendtion.xrichtext;
import android.animation.LayoutTransition;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import java.util.ArrayList;
import java.util.List;
/**
* 这是一个富文本编辑器,给外部提供insertImage接口,添加的图片跟当前光标所在位置有关
*
* @author xmuSistone
*
*/
public class RichTextEditor extends ScrollView {
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 LinearLayout allLayout; // 这个是所有子view的容器,scrollView内部的唯一一个ViewGroup
private LayoutInflater inflater;
private OnKeyListener keyListener; // 所有EditText的软键盘监听器
private OnClickListener btnListener; // 图片右上角红叉按钮监听器
private OnFocusChangeListener focusListener; // 所有EditText的焦点监听listener
private EditText lastFocusEdit; // 最近被聚焦的EditText
private LayoutTransition mTransitioner; // 只在图片View添加或remove时,触发transition动画
private int editNormalPadding = 0; //
private int disappearingImageIndex = 0;
public RichTextEditor(Context context) {
this(context, null);
}
public RichTextEditor(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RichTextEditor(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflater = LayoutInflater.from(context);
// 1. 初始化allLayout
allLayout = new LinearLayout(context);
allLayout.setOrientation(LinearLayout.VERTICAL);
//allLayout.setBackgroundColor(Color.WHITE);
//setupLayoutTransitions();//禁止载入动画
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
allLayout.setPadding(50,15,50,15);//设置间距,防止生成图片时文字太靠边,不能用margin,否则有黑边
addView(allLayout, layoutParams);
// 2. 初始化键盘退格监听
// 主要用来处理点击回删按钮时,view的一些列合并操作
keyListener = new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
EditText edit = (EditText) v;
onBackspacePress(edit);
}
return false;
}
};
// 3. 图片叉掉处理
btnListener = new OnClickListener() {
@Override
public void onClick(View v) {
RelativeLayout parentView = (RelativeLayout) v.getParent();
onImageCloseClick(parentView);
}
};
focusListener = new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
lastFocusEdit = (EditText) v;
}
}
};
LinearLayout.LayoutParams firstEditParam = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
//editNormalPadding = dip2px(EDIT_PADDING);
EditText firstEdit = createEditText("请输入内容", dip2px(context, EDIT_PADDING));
allLayout.addView(firstEdit, firstEditParam);
lastFocusEdit = firstEdit;
}
public int dip2px(Context context, float dipValue) {
float m = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * m + 0.5f);
}
/**
* 处理软键盘backSpace回退事件
*
* @param editTxt
* 光标所在的文本输入框
*/
private void onBackspacePress(EditText editTxt) {
int startSelection = editTxt.getSelectionStart();
// 只有在光标已经顶到文本输入框的最前方,在判定是否删除之前的图片,或两个View合并
if (startSelection == 0) {
int editIndex = allLayout.indexOfChild(editTxt);
View preView = allLayout.getChildAt(editIndex - 1); // 如果editIndex-1<0,
// 则返回的是null
if (null != preView) {
if (preView instanceof RelativeLayout) {
// 光标EditText的上一个view对应的是图片
onImageCloseClick(preView);
} else if (preView instanceof EditText) {
// 光标EditText的上一个view对应的还是文本框EditText
String str1 = editTxt.getText().toString();
EditText preEdit = (EditText) preView;
String str2 = preEdit.getText().toString();
// 合并文本view时,不需要transition动画
//allLayout.setLayoutTransition(null);
allLayout.removeView(editTxt);
//allLayout.setLayoutTransition(mTransitioner); // 恢复transition动画
// 文本合并
preEdit.setText(str2 + str1);
preEdit.requestFocus();
preEdit.setSelection(str2.length(), str2.length());
lastFocusEdit = preEdit;
}
}
}
}
/**
* 处理图片叉掉的点击事件
*
* @param view
* 整个image对应的relativeLayout view
* @type 删除类型 0代表backspace删除 1代表按红叉按钮删除
*/
private void onImageCloseClick(View view) {
//if (!mTransitioner.isRunning()) {
disappearingImageIndex = allLayout.indexOfChild(view);
//删除文件夹里的图片
// List<EditData> dataList = buildEditData();
// EditData editData = dataList.get(disappearingImageIndex);
// //Log.i("", "editData: "+editData);
// if (editData.imagePath != null){
// SDCardUtil.deleteFile(editData.imagePath);
// }
allLayout.removeView(view);
//}
}
public void clearAllLayout(){
allLayout.removeAllViews();
}
public int getLastIndex(){
//int lastEditIndex = allLayout.indexOfChild(lastFocusView);
int lastEditIndex = allLayout.getChildCount();
return lastEditIndex;
}
/**
* 生成文本输入框
*/
public EditText createEditText(String hint, int paddingTop) {
EditText editText = (EditText) inflater.inflate(R.layout.rich_edittext, null);
editText.setOnKeyListener(keyListener);
editText.setTag(viewTagIndex++);
editText.setPadding(editNormalPadding, paddingTop, editNormalPadding, paddingTop);
editText.setHint(hint);
editText.setOnFocusChangeListener(focusListener);
return editText;
}
/**
* 生成图片View
*/
private RelativeLayout createImageLayout() {
RelativeLayout layout = (RelativeLayout) inflater.inflate(
R.layout.edit_imageview, null);
layout.setTag(viewTagIndex++);
View closeView = layout.findViewById(R.id.image_close);
//closeView.setVisibility(GONE);
closeView.setTag(layout.getTag());
closeView.setOnClickListener(btnListener);
return layout;
}
/**
* 根据绝对路径添加view
*
* @param imagePath
*/
public void insertImage(String imagePath, int width) {
Bitmap bmp = getScaledBitmap(imagePath, width);
// Log.e("RichTextEditor", "###imgWidth=" + bmp.getWidth());
// Log.e("RichTextEditor", "###imgHeight=" + bmp.getHeight());
insertImage(bmp, imagePath);
}
/**
* 插入一张图片
*/
public void insertImage(Bitmap bitmap, String imagePath) {
String lastEditStr = lastFocusEdit.getText().toString();
int cursorIndex = lastFocusEdit.getSelectionStart();
String editStr1 = lastEditStr.substring(0, cursorIndex).trim();
int lastEditIndex = allLayout.indexOfChild(lastFocusEdit);
if (lastEditStr.length() == 0 || editStr1.length() == 0) {
// 如果EditText为空,或者光标已经顶在了editText的最前面,则直接插入图片,并且EditText下移即可
addImageViewAtIndex(lastEditIndex, bitmap, imagePath);
} else {
// 如果EditText非空且光标不在最顶端,则需要添加新的imageView和EditText
lastFocusEdit.setText(editStr1);
String editStr2 = lastEditStr.substring(cursorIndex).trim();
if (editStr2.length() == 0){
editStr2 = " ";
}
if (allLayout.getChildCount() - 1 == lastEditIndex ) {
addEditTextAtIndex(lastEditIndex + 1, editStr2);
}
addImageViewAtIndex(lastEditIndex + 1, bitmap, imagePath);
lastFocusEdit.requestFocus();
lastFocusEdit.setSelection(editStr1.length(), editStr1.length());//TODO
}
hideKeyBoard();
}
/**
* 隐藏小键盘
*/
public void hideKeyBoard() {
InputMethodManager imm = (InputMethodManager) getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(lastFocusEdit.getWindowToken(), 0);
}
/**
* 在特定位置插入EditText
*
* @param index
* 位置
* @param editStr
* EditText显示的文字
*/
public void addEditTextAtIndex(final int index, CharSequence editStr) {
EditText editText2 = createEditText("", EDIT_PADDING);
editText2.setText(editStr);
editText2.setOnFocusChangeListener(focusListener);
// 请注意此处,EditText添加、或删除不触动Transition动画
allLayout.setLayoutTransition(null);
allLayout.addView(editText2, index);
allLayout.setLayoutTransition(mTransitioner); // remove之后恢复transition动画
}
/**
* 在特定位置添加ImageView
*/
public void addImageViewAtIndex(final int index, Bitmap bmp,
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();
DataImageView imageView = (DataImageView) imageLayout
.findViewById(R.id.edit_imageView);
imageView.setImageBitmap(bmp);
imageView.setBitmap(bmp);
imageView.setAbsolutePath(imagePath);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);//裁剪剧中
// 调整imageView的高度
//int imageHeight = allLayout.getWidth() * bmp.getHeight() / bmp.getWidth();
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, 500);
lp.bottomMargin = 10;
imageView.setLayoutParams(lp);
// onActivityResult无法触发动画,此处post处理
allLayout.addView(imageLayout, index);
// allLayout.postDelayed(new Runnable() {
// @Override
// public void run() {
// allLayout.addView(imageLayout, index);
// }
// }, 200);
}
/**
* 根据view的宽度,动态缩放bitmap尺寸
*
* @param width
* view的宽度
*/
public Bitmap getScaledBitmap(String filePath, int width) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
int sampleSize = options.outWidth > width ? options.outWidth / width
+ 1 : 1;
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
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);
}
}
/**
* 对外提供的接口, 生成编辑数据上传
*/
public List<EditData> buildEditData() {
List<EditData> dataList = new ArrayList<EditData>();
int num = allLayout.getChildCount();
for (int index = 0; index < num; index++) {
View itemView = allLayout.getChildAt(index);
EditData itemData = new EditData();
if (itemView instanceof EditText) {
EditText item = (EditText) itemView;
itemData.inputStr = item.getText().toString();
} else if (itemView instanceof RelativeLayout) {
DataImageView item = (DataImageView) itemView.findViewById(R.id.edit_imageView);
itemData.imagePath = item.getAbsolutePath();
itemData.bitmap = item.getBitmap();
}
dataList.add(itemData);
}
return dataList;
}
public class EditData {
public String inputStr;
public String imagePath;
public Bitmap bitmap;
}
}
package com.sendtion.xrichtext;
import android.animation.LayoutTransition;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
/**
* Created by sendtion on 2016/6/24.
*/
public class RichTextView extends ScrollView {
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 LinearLayout allLayout; // 这个是所有子view的容器,scrollView内部的唯一一个ViewGroup
private LayoutInflater inflater;
private TextView lastFocusText; // 最近被聚焦的TextView
private LayoutTransition mTransitioner; // 只在图片View添加或remove时,触发transition动画
private int editNormalPadding = 0; //
private int disappearingImageIndex = 0;
public RichTextView(Context context) {
this(context, null);
}
public RichTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RichTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflater = LayoutInflater.from(context);
// 1. 初始化allLayout
allLayout = new LinearLayout(context);
allLayout.setOrientation(LinearLayout.VERTICAL);
//allLayout.setBackgroundColor(Color.WHITE);//去掉背景
//setupLayoutTransitions();//禁止载入动画
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
allLayout.setPadding(50,15,50,15);//设置间距,防止生成图片时文字太靠边
addView(allLayout, layoutParams);
LinearLayout.LayoutParams firstEditParam = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
//editNormalPadding = dip2px(EDIT_PADDING);
TextView firstText = createTextView("没有内容", dip2px(context, EDIT_PADDING));
allLayout.addView(firstText, firstEditParam);
lastFocusText = firstText;
}
public int dip2px(Context context, float dipValue) {
float m = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * m + 0.5f);
}
/**
* 清除所有的view
*/
public void clearAllLayout(){
allLayout.removeAllViews();
}
/**
* 获得最后一个子view的位置
* @return
*/
public int getLastIndex(){
int lastEditIndex = allLayout.getChildCount();
return lastEditIndex;
}
/**
* 生成文本输入框
*/
public TextView createTextView(String hint, int paddingTop) {
TextView textView = (TextView) inflater.inflate(R.layout.rich_textview, null);
textView.setTag(viewTagIndex++);
textView.setPadding(editNormalPadding, paddingTop, editNormalPadding, paddingTop);
textView.setHint(hint);
return textView;
}
/**
* 生成图片View
*/
private RelativeLayout createImageLayout() {
RelativeLayout layout = (RelativeLayout) inflater.inflate(
R.layout.edit_imageview, null);
layout.setTag(viewTagIndex++);
View closeView = layout.findViewById(R.id.image_close);
closeView.setVisibility(GONE);
return layout;
}
/**
* 在特定位置插入EditText
*
* @param index
* 位置
* @param editStr
* EditText显示的文字
*/
public void addTextViewAtIndex(final int index, CharSequence editStr) {
TextView textView = createTextView("", EDIT_PADDING);
textView.setText(editStr);
// 请注意此处,EditText添加、或删除不触动Transition动画
//allLayout.setLayoutTransition(null);
allLayout.addView(textView, index);
//allLayout.setLayoutTransition(mTransitioner); // remove之后恢复transition动画
}
/**
* 在特定位置添加ImageView
*/
public void addImageViewAtIndex(final int index, Bitmap bmp,
String imagePath) {
final RelativeLayout imageLayout = createImageLayout();
DataImageView imageView = (DataImageView) imageLayout
.findViewById(R.id.edit_imageView);
imageView.setImageBitmap(bmp);
imageView.setBitmap(bmp);
imageView.setAbsolutePath(imagePath);
// 调整imageView的高度
int imageHeight = allLayout.getWidth() * bmp.getHeight() / bmp.getWidth();
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, imageHeight);
lp.bottomMargin = 10;
imageView.setLayoutParams(lp);
// onActivityResult无法触发动画,此处post处理
allLayout.addView(imageLayout, index);
}
/**
* 根据view的宽度,动态缩放bitmap尺寸
*
* @param width
* view的宽度
*/
public Bitmap getScaledBitmap(String filePath, int width) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
int sampleSize = options.outWidth > width ? options.outWidth / width
+ 1 : 1;
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
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);
}
}
package com.sendtion.xrichtext;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.format.Time;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class SDCardUtil {
public static String SDCardRoot = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;
/**
* 检查是否存在SDCard
* @return
*/
public static boolean hasSdcard(){
String state = Environment.getExternalStorageState();
if(state.equals(Environment.MEDIA_MOUNTED)){
return true;
}else{
return false;
}
}
/**
* 获得文章图片保存路径
* @return
*/
public static String getPictureDir(){
String imageCacheUrl = SDCardRoot + "TimeNote" + File.separator + "Picture" + File.separator ;
File file = new File(imageCacheUrl);
if(!file.exists())
file.mkdir(); //如果不存在则创建
return imageCacheUrl;
}
/**
* 获得图片缓存路径
* @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
* @return
*/
public static String saveToSdCard(Bitmap bitmap) {
String imageUrl = getPictureDir() + getNoteImageName();
File file = new File(imageUrl);
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();
}
return file.getAbsolutePath();
}
/**
* 保存到指定路径,笔记中插入图片
* @param bitmap
* @param path
* @return
*/
public static String saveToSdCard(Bitmap bitmap, String path) {
//String fileUrl = getImageCacheDir() + File.separator + "head" + DateUtil.getTime2FileName() + ".jpg";
File file = new File(path);
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();
}
//System.out.println("文件保存路径:"+ file.getAbsolutePath());
return file.getAbsolutePath();
}
/**
* 根据Uri获取图片文件的绝对路径
*/
public static String getFilePathByUri(Context context, final Uri uri) {
if (null == uri) {
return null;
}
final String scheme = uri.getScheme();
String data = null;
if (scheme == null) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
Cursor cursor = context.getContentResolver().query(uri,
new String[] { MediaStore.Images.ImageColumns.DATA }, null, null, null);
if (null != cursor) {
if (cursor.moveToFirst()) {
int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
if (index > -1) {
data = cursor.getString(index);
}
}
cursor.close();
}
}
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;
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<com.sendtion.xrichtext.DataImageView
android:id="@+id/edit_imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY" />
<ImageView
android:id="@+id/image_close"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:paddingBottom="20dp"
android:paddingLeft="20dp"
android:paddingRight="5dp"
android:paddingTop="5dp"
android:scaleType="fitXY"
android:src="@drawable/icon_close" />
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<com.sendtion.xrichtext.DeletableEditText xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:cursorVisible="true"
android:lineSpacingExtra="8dp"
android:textSize="16sp"
android:textColor="#616161" />
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:cursorVisible="true"
android:lineSpacingExtra="8dp"
android:textIsSelectable="true"
android:textSize="16sp"
android:textColor="#616161" />
\ No newline at end of file
<resources>
<string name="app_name">xrichtext</string>
</resources>
package com.sendtion.xrichtext;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
\ No newline at end of file
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