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