You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
xiaomi/ui/NoteEditText.java

237 lines
9.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.content.Context;
import android.graphics.Rect;
import android.text.Layout;
import android.text.Selection;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.widget.EditText;
import net.micode.notes.R;
import java.util.HashMap;
import java.util.Map;
// 定义了一个名为NoteEditText的类它继承自Android SDK中的EditText类用于自定义笔记编辑文本框。
public class NoteEditText extends EditText {
// 类的成员变量,用于日志标记。
private static final String TAG = "NoteEditText";
// 索引变量,用于跟踪当前编辑的文本位置。
private int mIndex;
// 记录删除操作前光标的位置,用于处理删除操作。
private int mSelectionStartBeforeDelete;
// 定义几种常见的URL协议用于识别电话、http链接和邮件。
private static final String SCHEME_TEL = "tel:" ;
private static final String SCHEME_HTTP = "http:" ;
private static final String SCHEME_EMAIL = "mailto:" ;
// 一个映射将不同的URL协议映射到对应的字符串资源用于上下文菜单中显示。
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
static {
// 静态代码块,在类加载时执行,初始化映射关系。
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web);
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
}
// 定义一个内部接口,用于监听文本编辑的变化。
public interface OnTextViewChangeListener {
// 当删除键被按下且文本为空时调用。
void onEditTextDelete(int index, String text);
// 当回车键被按下时调用。
void onEditTextEnter(int index, String text);
// 当文本发生变化时调用。
void onTextChange(int index, boolean hasText);
}
// 接口的实例,用于回调文本编辑的变化。
private OnTextViewChangeListener mOnTextViewChangeListener;
// NoteEditText的构造函数只接收一个Context参数。
public NoteEditText(Context context) {
super(context, null);
mIndex = 0;
}
// 设置当前编辑文本的索引。
public void setIndex(int index) {
mIndex = index;
}
// 设置文本编辑变化的监听器。
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mOnTextViewChangeListener = listener;
}
// NoteEditText的构造函数接收Context和AttributeSet参数。
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle);
}
// NoteEditText的构造函数接收Context、AttributeSet和defStyle参数。
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// 这里有一个TODO注释提示这里是一个自动生成的构造函数体可能需要根据需要进行修改。
// TODO Auto-generated constructor stub
}
// 重写onTouchEvent方法处理触摸事件主要用于移动光标位置。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 计算触摸点的位置,并将其转换为文本中的偏移量。
int x = (int) event.getX();
int y = (int) event.getY();
x -= getTotalPaddingLeft();
y -= getTotalPaddingTop();
x += getScrollX();
y += getScrollY();
Layout layout = getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
Selection.setSelection(getText(), off);
break;
}
return super.onTouchEvent(event);
}
// 重写onKeyDown方法处理按键按下事件特别是回车和删除键。
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
// 如果设置了监听器,消费回车键事件。
if (mOnTextViewChangeListener != null) {
return false;
}
break;
case KeyEvent.KEYCODE_DEL:
// 记录删除前光标的位置。
mSelectionStartBeforeDelete = getSelectionStart();
break;
default:
break;
}
return super.onKeyDown(keyCode, event);
}
// 重写onKeyUp方法处理按键释放事件特别是删除和回车键。
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch(keyCode) {
case KeyEvent.KEYCODE_DEL:
// 如果设置了监听器,且光标在最开始位置,则触发删除事件。
if (mOnTextViewChangeListener != null) {
if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
return true;
}
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted");
}
break;
case KeyEvent.KEYCODE_ENTER:
// 如果设置了监听器,触发回车事件。
if (mOnTextViewChangeListener != null) {
int selectionStart = getSelectionStart();
String text = getText().subSequence(selectionStart, length()).toString();
setText(getText().subSequence(0, selectionStart));
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted");
}
break;
default:
break;
}
return super.onKeyUp(keyCode, event);
}
// 重写onFocusChanged方法处理焦点变化事件用于更新文本变化。
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (mOnTextViewChangeListener != null) {
if (!focused && TextUtils.isEmpty(getText())) {
// 如果焦点失去并且文本为空,通知监听器没有文本。
mOnTextViewChangeListener.onTextChange(mIndex, false);
} else {
// 如果焦点变化,通知监听器文本变化。
mOnTextViewChangeListener.onTextChange(mIndex, true);
}
}
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
// 重写onCreateContextMenu方法创建上下文菜单特别是处理URL链接。
@Override
protected void onCreateContextMenu(ContextMenu menu) {
if (getText() instanceof Spanned) {
int selStart = getSelectionStart();
int selEnd = getSelectionEnd();
int min = Math.min(selStart, selEnd);
int max = Math.max(selStart, selEnd);
// 获取选中的URLSpan对象。
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);
if (urls.length == 1) {
int defaultResId = 0;
for(String schema: sSchemaActionResMap.keySet()) {
// 根据URL协议找到对应的字符串资源。
if(urls[0].getURL().indexOf(schema) >= 0) {
defaultResId = sSchemaActionResMap.get(schema);
break;
}
}
if (defaultResId == 0) {
// 如果没有找到对应的协议,使用默认的字符串资源。
defaultResId = R.string.note_link_other;
}
// 添加上下文菜单项,并设置点击事件。
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
// 点击时执行URLSpan的onClick事件。
urls[0].onClick(NoteEditText.this);
return true;
}
});
}
}
super.onCreateContextMenu(menu);
}
}