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.
git-test/src/net/micode/notes/ui/NoteEditText.java

241 lines
9.2 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类拓展自EditText添加了一些特定用于笔记应用的特性
public class NoteEditText extends EditText {
//日志标签,用于调试
private static final String TAG = "NoteEditText";
//变量来存储当前编辑框的索引
private int mIndex;
//用于跟踪删除操作前光标位置
private int mSelectionStartBeforeDelete;
//协议模式,用于识别文本中的特殊内容
private static final String SCHEME_TEL = "tel:" ;
private static final String SCHEME_HTTP = "http:" ;
private static final String SCHEME_EMAIL = "mailto:" ;
//协议模式和资源ID映射用于创建上下文菜单项
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
static {
//将电话、网页、邮箱对应的文本资源ID填充到映射
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);
}
/**
* Call by the {@link NoteEditActivity} to delete or add edit text
*/
//接口,用于监听文本视图的改变事件,例如删除和输入
public interface OnTextViewChangeListener {
/**
* Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens
* and the text is null
*/
void onEditTextDelete(int index, String text);
/**
* Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER}
* happen
*/
void onEditTextEnter(int index, String text);
/**
* Hide or show item option when text change
*/
void onTextChange(int index, boolean hasText);
}
//用于通知文本视图更改的监听器
private OnTextViewChangeListener mOnTextViewChangeListener;
//构造函数初始化编辑框设置索引值为0
public NoteEditText(Context context) {
super(context, null);
mIndex = 0;
}
//设置编辑框的索引值
public void setIndex(int index) {
mIndex = index;
}
//设置监听器
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mOnTextViewChangeListener = listener;
}
//另外两个构造函数允许在创建对象使用布局属性和样式
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle);
}
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub 未实现的
}
//触摸事件的处理,主要是为了处理文本选择
@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);
}
//键盘按键事件的处理,主要是监听删除键和回车键
@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);
}
//松开按键事件的处理
@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);
}
//当编辑框焦点改变时,比如失去或者获取焦点时的处理
@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);
}
//上下文菜单创建时的处理,可以在编辑框中添加特定行为的菜单项,例如打电话、打开网页或发送邮箱
@Override
protected void onCreateContextMenu(ContextMenu menu) {
//检查文本内容是否为Spanned类型
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);
//如果只有一个URLSpan存在
if (urls.length == 1) {
int defaultResId = 0;
//遍历所有已定义的模式(schema)
for(String schema: sSchemaActionResMap.keySet()) {
//如果URLSpan的URL中包含该模式
if(urls[0].getURL().indexOf(schema) >= 0) {
//获取该模式对应的资源ID
defaultResId = sSchemaActionResMap.get(schema);
break;
}
}
//如果没有找到匹配的模式对应的资源ID则使用R.string.note_link_other作为默认资源ID
if (defaultResId == 0) {
defaultResId = R.string.note_link_other;
}
//像上下文菜单中添加菜单项,并设置点击事件
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
// goto a new intent 执行URLSpan中定义的点击操作
urls[0].onClick(NoteEditText.this);
return true;
}
});
}
}
super.onCreateContextMenu(menu); //调用父类的方法创建上下文菜单
}
}