Android编辑昵称、个性签名

Android编辑昵称、个性签名

一、目标

二、下载地址

神马笔记最新版本下载:【神马笔记 版本1.5.0——笔名功能.apk

三、功能设计

  • 标题栏组成
  1. 标题——显示当前编辑的目标
  2. 返回按钮——取消编辑内容
  3. 完成按钮——只有输入内容发生改变时可用,完成编辑内容
  • 内容界面组成
  1. 文本——编辑框默认内容
  2. 提示——编辑框的提示文本
  3. 名称——显示在编辑框左上角,简短说明
  4. 解释——显示在编辑框左下角,进一步说明
  • 输入文本限定
  1. 限定文本最大长度——默认不限定
  2. 限定文本最小行数——默认为1行
  3. 限定文本最大行数——默认不限定

四、准备工作

编辑昵称、个性签名使用EditText即可完成,毫无技术难度。

常见的界面实现方式有2种——局部和全屏。

  • 左侧——聊天宝以弹出局部对话框的方式编辑名称

  • 右侧——微信以全屏的方式编辑名称

界面实现是选择局部,还是全屏?这是个有趣的问题。

从实现方式上考虑,二者都没有什么技术难度,开发时间也相差无几。

从用户体验上考虑,也是萝卜青菜各有所爱,分不出优劣。

最后选择的实现方式——全屏

理由——切换输入法时,局部对话框会上下移动。

实在不喜欢对话框发生移动,因此最终选择全屏方式。

五、组合起来

1. 布局文件

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".preference.ComposeTextFragment">

<app.haiyunshan.whatsnote.widget.SearchTitleBar
android:id="@+id/title_bar"
android:layout_width="match_parent"
app:searchVisible="false"
android:layout_height="wrap_content"
android:background="@drawable/shape_preference_top_bar_bg">

</app.haiyunshan.whatsnote.widget.SearchTitleBar>

<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:overScrollMode="never">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="?listPreferredItemPaddingLeft"
android:paddingBottom="?listPreferredItemPaddingRight">

<include
android:id="@+id/tv_name"
layout="@layout/layout_setting_title_list_item"
android:layout_marginBottom="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:gravity="top|left"
android:paddingLeft="?listPreferredItemPaddingLeft"
android:paddingRight="?listPreferredItemPaddingRight"
android:background="@drawable/shape_compose_text_bg">
<requestFocus/>
</EditText>

<include
android:id="@+id/tv_explain"
layout="@layout/layout_setting_explain_list_item"
android:layout_marginTop="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>

2. Fragment

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
package app.haiyunshan.whatsnote.preference;


import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import app.haiyunshan.whatsnote.R;
import app.haiyunshan.whatsnote.base.MenuItemClickListener;
import app.haiyunshan.whatsnote.widget.SearchTitleBar;
import club.andnext.helper.ClearAssistMenuHelper;
import club.andnext.ucrop.BuildConfig;

import java.util.Arrays;

/**
* A simple {@link Fragment} subclass.
*/
public class ComposeTextFragment extends Fragment {

private static final String EXTRA_PREFIX = BuildConfig.APPLICATION_ID;

SearchTitleBar titleBar;

TextView nameView;
EditText editText;
TextView explainView;

String text;
MenuItem doneMenuItem;

public ComposeTextFragment() {

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_compose_text, container, false);
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);

{
this.titleBar = view.findViewById(R.id.title_bar);

Toolbar toolbar = titleBar.getToolbar();
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp);
toolbar.setNavigationOnClickListener(this::onNavigationClick);

toolbar.inflateMenu(R.menu.menu_compose);
toolbar.setOnMenuItemClickListener(new MenuItemClickListener().put(R.id.menu_done, this::onDoneClick));

this.doneMenuItem = toolbar.getMenu().findItem(R.id.menu_done);
doneMenuItem.setEnabled(false);
}

{
this.nameView = view.findViewById(R.id.tv_name);
this.editText = view.findViewById(R.id.edit_text);
this.explainView = view.findViewById(R.id.tv_explain);
}

{
ClearAssistMenuHelper.attach(editText);

this.editText.addTextChangedListener(new ComposeTextListener());
}
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);

Bundle args = this.getArguments();

{
CharSequence cs = args.getCharSequence(Builder.EXTRA_TEXT);

this.text = (TextUtils.isEmpty(cs))? "": cs.toString();
}

{
CharSequence title = args.getCharSequence(Builder.EXTRA_TITLE);
titleBar.setTitle(title);
}

{
int maxLength = args.getInt(Options.EXTRA_MAX_LENGTH, -1);
if (maxLength > 0) {
InputFilter[] filters = editText.getFilters();

{
filters = Arrays.copyOf(filters, filters.length + 1);
filters[filters.length - 1] = new InputFilter.LengthFilter(maxLength);
}

editText.setFilters(filters);
}

int minLines = args.getInt(Options.EXTRA_MIN_LINES, -1);
if (minLines > 0) {
editText.setMinLines(minLines);
}

int maxLines = args.getInt(Options.EXTRA_MAX_LINES, -1);
if (maxLines > 0) {
editText.setMaxLines(maxLines);
}

if (minLines <= 1 && maxLines <= 1) {
editText.setSingleLine(true);
}

}

{
CharSequence hint = args.getCharSequence(Options.EXTRA_HINT);
editText.setHint(hint);
}

{
CharSequence text = args.getCharSequence(Options.EXTRA_NAME);
nameView.setVisibility(TextUtils.isEmpty(text)? View.GONE: View.VISIBLE);
nameView.setText(text);
}

{
CharSequence text = args.getCharSequence(Options.EXTRA_EXPLAIN);
explainView.setVisibility(TextUtils.isEmpty(text)? View.GONE: View.VISIBLE);
explainView.setText(text);
}

{
editText.setText(text);
editText.setSelection(text.length());
}

}

void onNavigationClick(View view) {
getActivity().onBackPressed();
}

void onDoneClick(MenuItem item) {
Intent intent = new Intent();
intent.putExtra(Builder.EXTRA_TEXT, editText.getText().toString());

getActivity().setResult(Activity.RESULT_OK, intent);
getActivity().onBackPressed();
}

/**
*
*/
private class ComposeTextListener implements TextWatcher {

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {

}

@Override
public void afterTextChanged(Editable s) {
boolean equals = (text.equals(s.toString()));
doneMenuItem.setEnabled(!equals);
}
}

/**
*
*/
public static class Builder {

public static final String EXTRA_TITLE = EXTRA_PREFIX + ".Title";
public static final String EXTRA_TEXT = EXTRA_PREFIX + ".Text";

protected Intent intent;
protected Bundle bundle;

public Builder(@NonNull CharSequence title, @NonNull CharSequence text) {
this.intent = new Intent();

this.bundle = new Bundle();
bundle.putCharSequence(EXTRA_TITLE, title);
bundle.putCharSequence(EXTRA_TEXT, text);
}

public Builder withOptions(@NonNull Options options) {
bundle.putAll(options.getOptionBundle());

return this;
}
}

/**
*
*/
public static class Options {

public static final String EXTRA_NAME = EXTRA_PREFIX + ".Name";
public static final String EXTRA_HINT = EXTRA_PREFIX + ".Hint";
public static final String EXTRA_EXPLAIN = EXTRA_PREFIX + ".Explain";

public static final String EXTRA_MAX_LENGTH = EXTRA_PREFIX + ".MaxLength";

public static final String EXTRA_MIN_LINES = EXTRA_PREFIX + ".MinLines";
public static final String EXTRA_MAX_LINES = EXTRA_PREFIX + ".MaxLines";

private final Bundle mOptionBundle;

public Options() {
mOptionBundle = new Bundle();
}

@NonNull
public Bundle getOptionBundle() {
return mOptionBundle;
}

public void setName(CharSequence name) {
mOptionBundle.putCharSequence(EXTRA_NAME, name);
}

public void setHint(CharSequence hint) {
mOptionBundle.putCharSequence(EXTRA_HINT, hint);
}

public void setExplain(CharSequence explain) {
mOptionBundle.putCharSequence(EXTRA_EXPLAIN, explain);
}

public void setMaxLength(int length) {
mOptionBundle.putInt(EXTRA_MAX_LENGTH, length);
}

public void setMinLines(int value) {
mOptionBundle.putInt(EXTRA_MIN_LINES, value);
}

public void setMaxLines(int value) {
mOptionBundle.putInt(EXTRA_MAX_LINES, value);
}

}
}

这里借鉴了uCrop的设计方式。

Builder传入必须的2个参数——Title和Text。这2个为必须参数。

Options提供了设置可选参数接口——名称、提示、注解以及最大长度、最小行数、最大行数共6个信息。

六、Finally

~白首相知犹按剑~朱门先达笑弹冠~