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.

265 lines
8.9 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;
/**
* 笔记编辑文本框
* 继承自EditText提供笔记编辑的特殊功能
* 支持链接识别、键盘事件处理、文本变化监听等
*/
public class NoteEditText extends EditText {
// 日志标签
private static final String TAG = "NoteEditText";
// 当前编辑框的索引
private int mIndex;
// 删除前的选择起始位置
private int mSelectionStartBeforeDelete;
// URL协议常量
private static final String SCHEME_TEL = "tel:" ; // 电话协议
private static final String SCHEME_HTTP = "http:" ; // HTTP协议
private static final String SCHEME_EMAIL = "mailto:" ; // 邮件协议
// 协议到操作资源的映射表
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); // 邮件链接
}
/**
* 文本视图变化监听器接口
* 由{@link NoteEditActivity}调用来删除或添加编辑文本
*/
public interface OnTextViewChangeListener {
/**
* 当{@link KeyEvent#KEYCODE_DEL}发生且文本为空时删除当前编辑文本
* @param index 编辑框索引
* @param text 文本内容
*/
void onEditTextDelete(int index, String text);
/**
* 当{@link KeyEvent#KEYCODE_ENTER}发生时在当前编辑文本后添加编辑文本
* @param index 编辑框索引
* @param text 文本内容
*/
void onEditTextEnter(int index, String text);
/**
* 当文本变化时隐藏或显示项目选项
* @param index 编辑框索引
* @param hasText 是否有文本
*/
void onTextChange(int index, boolean hasText);
}
// 文本视图变化监听器
private OnTextViewChangeListener mOnTextViewChangeListener;
/**
* 构造方法
* @param context 上下文对象
*/
public NoteEditText(Context context) {
super(context, null);
mIndex = 0;
}
/**
* 设置编辑框索引
* @param index 索引值
*/
public void setIndex(int index) {
mIndex = index;
}
/**
* 设置文本视图变化监听器
* @param listener 监听器对象
*/
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mOnTextViewChangeListener = listener;
}
/**
* 构造方法(带属性)
* @param context 上下文对象
* @param attrs 属性集合
*/
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle);
}
/**
* 构造方法(带样式)
* @param context 上下文对象
* @param attrs 属性集合
* @param defStyle 默认样式
*/
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// 自动生成的构造方法存根
}
/**
* 触摸事件处理
* 处理触摸位置的光标定位
* @param event 触摸事件
* @return 是否处理了事件
*/
@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) {
if (getText() instanceof Spanned) {
int selStart = getSelectionStart();
int selEnd = getSelectionEnd();
int min = Math.min(selStart, selEnd);
int max = Math.max(selStart, selEnd);
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);
if (urls.length == 1) {
int defaultResId = 0;
for(String schema: sSchemaActionResMap.keySet()) {
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) {
// goto a new intent
urls[0].onClick(NoteEditText.this);
return true;
}
});
}
}
super.onCreateContextMenu(menu);
}
}