Android Marshmallow实现文字选中
2016-06-30 18:10
537 查看
在浏览器里,我们经常在网页上看到中意的内容,想选择特定的内容,完后复制,再在其它地方粘贴,这是很常见的功能,不过由于是在网页中,所以理论上来说,应该是用JavaScript或者其它类似的技术实现的,而不是Android,今天在看Marshmallow的文档时,意外发现新推出了Text Selection的功能,就学习了一下,自己写了个小例子,先看效果图:
功能很简单,就是上面一个TextView,选中时会弹出一个自定义的菜单,选择要复制的内容,完后选择“复制”,再在下面的EditText中长按,选择粘贴,将刚才复制的内容粘贴到EditText的指定位置,另外,这里复制不仅仅针对本应用,在其它地方也可以粘贴,因为复制的内容放到剪切板里面去了。注意,这是Marshmallow引入的新功能,其它版本应该是看不到效果的。
具体实现步骤如下:
1. 创建布局文件
这没什么好说的,上面一个TextView,下面一个EditText
2. 创建菜单文件
这个也很简单,两个菜单,复制和粘贴
3. 创建ActionMode.Callback
这个也不是什么新方法,在3.0版本就引入了,主要需要实现四个回调
(1)
(3)
再看粘贴,粘贴在EditText处理,粘贴时,首先获取当前光标的位置,再获取EditText当前的内容,完后在光标位置插入我们刚才保存在剪切板里面的内容即可。
(4)
4. 设置EditText的长按事件
5. 指定TextView的选择属性
通过以上的几步,我们就可以实现效果图中的效果了,完整MainActivity.java如下:
源码下载
功能很简单,就是上面一个TextView,选中时会弹出一个自定义的菜单,选择要复制的内容,完后选择“复制”,再在下面的EditText中长按,选择粘贴,将刚才复制的内容粘贴到EditText的指定位置,另外,这里复制不仅仅针对本应用,在其它地方也可以粘贴,因为复制的内容放到剪切板里面去了。注意,这是Marshmallow引入的新功能,其它版本应该是看不到效果的。
具体实现步骤如下:
1. 创建布局文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/copyArea" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="测试Android的Marshmallow版本提供的Text Selection功能" android:textSize="20sp"/> <EditText android:id="@+id/pasteArea" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
这没什么好说的,上面一个TextView,下面一个EditText
2. 创建菜单文件
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/copy" android:title="复制"/> <item android:id="@+id/paste" android:title="粘贴"/> </menu>
这个也很简单,两个菜单,复制和粘贴
3. 创建ActionMode.Callback
这个也不是什么新方法,在3.0版本就引入了,主要需要实现四个回调
(1)
@Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { MenuInflater inflater = mode.getMenuInflater(); inflater.inflate(R.menu.main, menu); return true; }(2)创建的时候,将第2步中创建的菜单引入
@Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { for (int i = 0; i < menu.size(); i++) { MenuItem item = menu.getItem(i); if (!menuIds.contains(item.getItemId())) item.setVisible(false); } return false; }这里的menuIds是一个List,将我们自定义的两个菜单的id存入,之所以需要这个,是因为Android系统默认已经指定了几个菜单,默认我们的菜单是加在指定的几个菜单后面的,所以这里需要将Android默认的菜单隐藏起来。
(3)
@Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch (item.getItemId()) { case R.id.copy: int min = 0; int max = copyArea.getText().length(); if (copyArea.isFocused()) { final int start = copyArea.getSelectionStart(); final int end = copyArea.getSelectionEnd(); min = Math.max(0, Math.min(start, end)); max = Math.max(0, Math.max(start, end)); } cmb.setPrimaryClip(ClipData.newPlainText("paste_content", copyArea.getText().subSequence(min, max))); mode.finish(); return true; case R.id.paste: if (pasteArea.isFocusable()) { int index = pasteArea.getSelectionStart(); Editable editable = pasteArea.getText(); editable.insert(index, cmb.getPrimaryClip().getItemAt(0).coerceToText(MainActivity.this)); } mode.finish(); return true; } return false; }这里是实现的重点,也就是点击“复制”和“粘贴”菜单时,所需要做的操作,先看复制,copyArea就是上面的TextView,也就是我们需要复制的部分,当我们点了“复制”按钮时,通过getSelectionStart和getSelectionEnd方法,我们可以获取到我们选择的内容,注意这两个方法在没有选择的时候,都有可能返回-1,出于程序健壮性的考虑,这里做了一些数学上的判断,确保获取到正确选择的内容。而cmb是一个ClipboardManager对象,也就是剪切板,通过它,我们可以将复制的内容保存在系统里,在应用外也可以使用,最后的mode.finish用于关闭菜单。
再看粘贴,粘贴在EditText处理,粘贴时,首先获取当前光标的位置,再获取EditText当前的内容,完后在光标位置插入我们刚才保存在剪切板里面的内容即可。
(4)
@Override public void onDestroyActionMode(ActionMode mode) { actionMode = null; }最后这个是在菜单destroy时,将actionMode赋值为Null,以便gc回收,这个actionMode是在下面长按EditText时声明的
4. 设置EditText的长按事件
pasteArea.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { if (actionMode != null) { return false; } actionMode = startActionMode(callback, ActionMode.TYPE_FLOATING); return true; } });actionMode不为null表示菜单已经弹出了,直接返回,这里通过startActionMode方法,指定第3步中声明的callback,从而弹出刚才的菜单,后面一个参数TYPE_FLOATING是指浮动菜单
5. 指定TextView的选择属性
copyArea.setTextIsSelectable(true); copyArea.setCustomSelectionActionModeCallback(callback);
通过以上的几步,我们就可以实现效果图中的效果了,完整MainActivity.java如下:
public class MainActivity extends AppCompatActivity {
private ActionMode actionMode;
private ActionMode.Callback callback;
private TextView copyArea;
private EditText pasteArea;
private List<Integer> menuIds = new ArrayList<>();
private ClipboardManager cmb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
menuIds.add(R.id.copy);
menuIds.add(R.id.paste);
copyArea = (TextView) findViewById(R.id.copyArea);
pasteArea = (EditText) findViewById(R.id.pasteArea);
cmb = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
callback = new ActionMode.Callback() {
@Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { MenuInflater inflater = mode.getMenuInflater(); inflater.inflate(R.menu.main, menu); return true; }
@Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { for (int i = 0; i < menu.size(); i++) { MenuItem item = menu.getItem(i); if (!menuIds.contains(item.getItemId())) item.setVisible(false); } return false; }
@Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch (item.getItemId()) { case R.id.copy: int min = 0; int max = copyArea.getText().length(); if (copyArea.isFocused()) { final int start = copyArea.getSelectionStart(); final int end = copyArea.getSelectionEnd(); min = Math.max(0, Math.min(start, end)); max = Math.max(0, Math.max(start, end)); } cmb.setPrimaryClip(ClipData.newPlainText("paste_content", copyArea.getText().subSequence(min, max))); mode.finish(); return true; case R.id.paste: if (pasteArea.isFocusable()) { int index = pasteArea.getSelectionStart(); Editable editable = pasteArea.getText(); editable.insert(index, cmb.getPrimaryClip().getItemAt(0).coerceToText(MainActivity.this)); } mode.finish(); return true; } return false; }
@Override public void onDestroyActionMode(ActionMode mode) { actionMode = null; }
};
copyArea.setTextIsSelectable(true);
copyArea.setCustomSelectionActionModeCallback(callback);
pasteArea.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (actionMode != null) {
return false;
}
actionMode = startActionMode(callback, ActionMode.TYPE_FLOATING);
return true;
}
});
}
}
源码下载
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories