版权声明:个人原创,欢迎转载。 https://blog.csdn.net/chuyangchangxi/article/details/86749821
一、目标
二、下载地址
神马笔记最新版本下载:【神马笔记 版本1.5.0——笔名功能.apk】
三、功能设计
- 标题栏组成
- 标题——显示当前编辑的目标
- 返回按钮——取消编辑内容
- 完成按钮——只有输入内容发生改变时可用,完成编辑内容
- 内容界面组成
- 文本——编辑框默认内容
- 提示——编辑框的提示文本
- 名称——显示在编辑框左上角,简短说明
- 解释——显示在编辑框左下角,进一步说明
- 输入文本限定
- 限定文本最大长度——默认不限定
- 限定文本最小行数——默认为1行
- 限定文本最大行数——默认不限定
四、准备工作
编辑昵称、个性签名使用EditText
即可完成,毫无技术难度。
常见的界面实现方式有2种——局部和全屏。
-
左侧——聊天宝以弹出局部对话框的方式编辑名称
-
右侧——微信以全屏的方式编辑名称
界面实现是选择局部,还是全屏?这是个有趣的问题。
从实现方式上考虑,二者都没有什么技术难度,开发时间也相差无几。
从用户体验上考虑,也是萝卜青菜各有所爱,分不出优劣。
最后选择的实现方式——全屏!
理由——切换输入法时,局部对话框会上下移动。
实在不喜欢对话框发生移动,因此最终选择全屏方式。
五、组合起来
1. 布局文件
<?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
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
~白首相知犹按剑~朱门先达笑弹冠~