Android的EditText很强大,支持文本缩进,字体设置以及图片的插入,需求很简单,参照有道云笔记……说得简单,有道云笔记反编译出来,看到libynote_lib.so和libynote_lib_img.so这两个库文件心顿时就凉了一半,我们这几只小菜鸟还只是处于应用层开发,不过拼死拼活了大半个月,总算马马虎虎的做出来了,现对这个做一个总结。总结的目的是对该任务的一个记录,该文章也只是个建议及引导,希望对读者有所帮助。
需求:可以使用有道云笔记大致玩一玩,不得不说这个app确实做得好,虽说也有很多bug,但是使用简便,功能强大,对于那些bug自然而然也就不在意了。
a. 序号排版:1.支持段落编号,当当前行有编号时,点击回车键,下一行能够自动显示编号并且+1;2.支持多级缩进编号。
b.图片的添加:1.支持添加@XXX,@XXX作为一个整体,光标只能选择在这个整体之前或者之后,删除的时候必须整体删除;2.支持添加大图片功能。
c.字体设置:1.支持文本的加粗、斜体、颜色、下划线等。
d.键盘的显示和隐藏:1.支持点击打开键盘,再次点击关闭键盘。
方案的选择
a. 使用多个EditText纵向排列,每个作为一个对象,每个可以管理自己的二级段落,递归管理。这样的好处是方便管理,并且添加的图片可以以控件的形式添加,放大缩小功能等都容易实现;缺点也很明显,无法跨多个EditText选择文本。
b. span相关使用,详情请点这里→_→《Spans,一个强大的概念》,牛人的总结非常详细;这个可以很好地实现我们想要的效果,但是也有一定的缺陷。
实现
a.编号的序号。
编号分三类,序号类1234,abcd,I II罗马数字;无序类●○□;无编号类。其中无序类容易实现,序号类比较麻烦,先给出序号类编号算法。
先给出接口父类IOrderIndex.java
public interface IOrderIndex {
public String getIndexString(int index);
}
数字类NumberIndex.java
public class NumberIndex implements IOrderIndex{
@Override
public String getIndexString(int index) {
return index + "";
}
}
英文字母类EnglishLetterIndex.java
public class EnglishLetterIndex implements IOrderIndex {
String[] array = new String[] { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" };
@Override
public String getIndexString(int index) {
String result = "";
result = switchNumToLetter(index);
return result;
}
public String switchNumToLetter(int index) {
if (index <= 0) {
return "";
}
String out = "";
int count = 26;
if (index > 0 && index <= count) {
out = array[index - 1];
return out;
} else {
if (index % count == 0) {
out = switchNumToLetter(index / count - 1) + array[count - 1];
} else {
out = switchNumToLetter(index / count) + switchNumToLetter(index - (index / count) * count);
}
}
return out;
}
}
罗马数字类RomanNumberIndex.java
public class RomanNumberIndex implements IOrderIndex{
public static final String TAG = "note";
@Override
public String getIndexString(int index) {
String result = "";
result = intToRoman(index);
return result;
}
/**
* @param s
* - String Roman
* @return int number
*/
public int romanToInt(String s) {
if (s.length() < 1)
return 0;
int result = 0;
int current = 0;
int pre = singleRomanToInt(s.charAt(0));
int temp = pre;
for (int i = 1; i < s.length(); i++) {
current = singleRomanToInt(s.charAt(i));
if (current == pre)
temp += current;
else if (current > pre) {
temp = current - temp;
} else if (current < pre) {
result += temp;
temp = current;
}
pre = current;
}
result += temp;
return result;
}
/**
* @param c
* single Roman
* @return single number
*/
public int singleRomanToInt(char c) {
switch (c) {
case 'I':
return 1;
case 'V':
return 5;
case 'X':
return 10;
case 'L':
return 50;
case 'C':
return 100;
case 'D':
return 500;
case 'M':
return 1000;
default:
return 0;
}
}
/**
* @param n
* - input single int
* @param nth
* must start from 1; 1 <= nth <= 4
* @return String single Roman
*/
public String singleDigitToRoman(int n, int nth) {
if (n == 0) {
return "";
}
nth = 2 * nth - 1; // nth must start from 1
char singleRoman[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M', 'Z', 'E' }; // never
// use
// 'Z'
// &
// 'E'
StringBuilder rsb = new StringBuilder("");
if (n <= 3) {
for (int i = 0; i < n; i++) {
rsb.append(singleRoman[nth - 1]);
}
return rsb.toString();
}
if (n == 4) {
rsb.append(singleRoman[nth - 1]);
rsb.append(singleRoman[nth]);
return rsb.toString();
}
if (n == 5) {
return singleRoman[nth] + "";
}
if (n >= 6 && n <= 8) {
rsb.append(singleRoman[nth]);
for (int i = 0; i < (n - 5); i++) {
rsb.append(singleRoman[nth - 1]);
}
return rsb.toString();
}
if (n == 9) {
rsb.append(singleRoman[nth - 1]);
rsb.append(singleRoman[nth + 1]);
return rsb.toString();
}
return "ERROR!!!";
}
/**
* @param num
* - input number within range 1 ~ 3999
* @return String Roman number
*/
public String intToRoman(int num) {
if (num < 1 || num > 3999) {
Log.e(TAG, "ERROR input number is 1 ~ 3999");
return "";
}
int temp = num;
String singleRoman[] = { "", "", "", "" };
StringBuilder result = new StringBuilder("");
int digits = 0; // 1 ~ 4
while (temp != 0) {
temp = temp / 10;
digits++;
}
temp = num;
int[] singleInt = new int[digits];
for (int i = 0; i < digits; i++) {
singleInt[i] = temp % 10;
singleRoman[i] = singleDigitToRoman(temp % 10, i + 1);
temp /= 10;
}
for (int i = digits - 1; i >= 0; i--) {
result.append(singleRoman[i]);
}
return result.toString();
}
}
无序类非常简单
空心圆,属于特殊字符CircleHollowPointIndex.java
public class CircleHollowPointIndex implements IOrderIndex{
@Override
public String getIndexString(int index) {
// TODO Auto-generated method stub
return "○";
}
}
实心圆CircleSolidPointIndex.java
public class CircleSolidPointIndex implements IOrderIndex{
@Override
public String getIndexString(int index) {
// TODO Auto-generated method stub
return "●";
}
}
正方形实心SquareSolidPointIndex.java
public class SquareSolidPointIndex implements IOrderIndex{
@Override
public String getIndexString(int index) {
// TODO Auto-generated method stub
return "■";
}
}
最后是空类型NoneIndex.java
public class NoneIndex implements IOrderIndex{
@Override
public String getIndexString(int index) {
// TODO Auto-generated method stub
return "";
}
}
编号的基本生成编码完成,下一篇使用抽象工厂将这些东西组合起来,使用起来更方便。