神马笔记 版本1.7.0——辅助编辑·代码篇

神马笔记 版本1.7.0——辅助编辑·代码篇

一、目标

定义实现辅助编辑的整体代码框架。

二、体验地址

神马笔记最新版本下载:【神马笔记 版本1.7.0——辅助编辑功能.apk

三、代码结构

与辅助编辑有关的代码均位于app.haiyunshan.whatsnote.outline包之下。

1. 包结构

  • app.haiyunshan.whatsnote.outline
    • OutlineFragment
    • IndentDialogFragment
    • IndentFragment
  • app.haiyunshan.whatsnote.outline.entity
    • Outline
    • BaseOutlineEntity
    • ParagraphOutlineEntity
    • PictureOutlineEntity
  • app.haiyunshan.whatsnote.outline.viewholder
    • BaseOutlineViewHolder
    • ParagraphOutlineViewHolder
    • PictureOutlineViewHolder
  • app.haiyunshan.whatsnote.outline.helper
    • OutlineHelper
    • OutlineFactory
Package Summary
app.haiyunshan.whatsnote.outline Fragment级别的用户交互类
app.haiyunshan.whatsnote.outline.entity 交互数据类
app.haiyunshan.whatsnote.outline.viewholder RecyclerView的ViewHolder类
app.haiyunshan.whatsnote.outline.helper 助手类

2. 界面类

app.haiyunshan.whatsnote.outline Summary
OutlineFragment 大纲结构界面
IndentDialogFragment 段落缩进对话框
IndentFragment 段落缩进界面

3. 交互数据类

app.haiyunshan.whatsnote.outline.entity Summary
Outline 文章大纲数据集合类
BaseOutlineEntity 段落和图片基类
ParagraphOutlineEntity 段落类
PictureOutlineEntity 图片类

4. ViewHolder类

app.haiyunshan.whatsnote.outline.viewholder Summary
BaseOutlineViewHolder 段落和图片基类,与BaseOutlineEntity对应。
ParagraphOutlineViewHolder 段落类,与ParagraphOutlineEntity对应。
PictureOutlineViewHolder 图片类,与PictureOutlineEntity对应。

5. 助手类

app.haiyunshan.whatsnote.outline.helper Summary
OutlineHelper 大纲结构管理助手类。
OutlineFactory 大纲结构工厂类,负责Document与Outline的相互转换。

四、布局资源

1. 列表项布局

段落和图片的布局唯一的差别只是显示内容控件的不同,前者为TextView,后者为ImageView。其他所有布局都是相同的,因此定义通用的布局资源,预留ViewStub用于段落和图片加载自身的布局资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/settingItemHeight"
android:background="@color/colorWindowBackground">

<FrameLayout
android:id="@+id/btn_delete"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_red_light"
android:foreground="?selectableItemBackground">

<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:layout_gravity="left"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:text="删除"
android:textStyle="bold"
android:textColor="@android:color/white"
android:textAppearance="@style/TextAppearance.AppCompat.Button"/>

</FrameLayout>

<LinearLayout
android:id="@+id/swipe_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="@dimen/settingItemHeight"
android:paddingLeft="?listPreferredItemPaddingLeft"
android:paddingRight="?listPreferredItemPaddingRight"
android:background="@color/colorWindowBackground"
android:foreground="?selectableItemBackground"
android:clipToPadding="false"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_gravity="center">

<ImageView
android:id="@+id/iv_checkbox"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@null"
android:background="@drawable/anc_ic_circle_color_stroke"
android:tint="@color/colorPrimary"
android:backgroundTint="@color/colorChevron"
android:scaleType="fitCenter"
android:layout_gravity="left|center_vertical"
android:visibility="visible"
android:duplicateParentState="false"
android:layout_marginRight="?listPreferredItemPaddingRight"/>

<ImageView
android:id="@+id/iv_icon"
android:layout_marginRight="?listPreferredItemPaddingRight"
android:layout_width="24dp"
android:layout_height="24dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_paragraph_outline"
android:tint="@color/colorOutlineIcon"
android:visibility="gone"
/>

<ViewStub
android:id="@+id/stub"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="left|center_vertical"/>

<ImageView
android:id="@+id/btn_drag"
android:layout_width="28dp"
android:layout_height="28dp"
android:src="@drawable/ic_drag_handle_white_24dp"
android:tint="@color/colorDrag"
android:scaleType="centerInside"
android:visibility="visible"
android:layout_gravity="right|center_vertical"
android:layout_marginLeft="?listPreferredItemPaddingLeft"/>

</LinearLayout>

</FrameLayout>

2. 段落布局

替换列表项布局中的ViewStub

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv_name"
android:textAppearance="@style/TextAppearance.AppCompat.Menu"
android:layout_width="0dp"
android:gravity="left|center_vertical"
android:layout_height="match_parent"
android:singleLine="true"
android:lines="1"
android:layout_weight="1"/>

3. 图片布局

替换列表项布局中的ViewStub

设置scaleType为fitXY,伸缩图片适应控件大小。

设置background为圆角矩形,并配合outlineProvider实现裁剪,实现圆角图片效果。

设置foreground,为控件添加Stroke。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent">

<ImageView
android:id="@+id/iv_picture"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:scaleType="fitXY"
android:background="@drawable/shape_picture_outline_bg"
android:foreground="@drawable/shape_picture_outline_fg"
android:outlineProvider="background"/>

</FrameLayout>

五、实现要点

1. OutlineFactory

OutlineFactory实现了DocumentOutline的相互转换。需要注意的是Outline转换为Document时。

  1. 内容不能为空
  2. 最后一个必须是段落,以保证用户可以继续编辑
  3. 第一个必须是段落,以保证用户可以编写标题

2. BaseOutlineEntity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class BaseOutlineEntity<T extends DocumentEntity> {

String id;

T parent;

BaseOutlineEntity(T parent) {
this.parent = parent;

this.id = UUIDUtils.next();
}

public String getId() {
return this.id;
}

public String getParentId() {
return parent.getId();
}

public T getParent() {
return parent;
}

public void indent(CharSequence prefix) {
// 由子类实现缩进
}

public void delete() {
// 由子类实现删除
}
}

3. BaseOutlineViewHolder

由于通过反射方式创建BaseOutlineViewHolder的子类们,必须保证构造函数不被混淆。因此必须使用@Keep注解标注构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class BaseOutlineViewHolder<T extends BaseOutlineEntity> extends SwipeViewHolder<T> {

public static final int LAYOUT_RES_ID = R.layout.layout_outline_list_item;

ImageView checkBox;
ImageView iconView;
ViewStub viewStub;
ImageView dragView;

OutlineHelper helper;

@Keep
public BaseOutlineViewHolder(OutlineHelper helper, View itemView) {
super(itemView);

this.helper = helper;
}

@Override
public int getLayoutResourceId() {
return LAYOUT_RES_ID;
}

// ……
}

六、实现过程

欲知后事如何,且听下回分解。

七、Finally

~还似旧时游上苑~车如流水马如龙~