您的位置:首页 > 移动开发 > Android开发

Android Marshmallow实现文字选中

2016-06-30 18:10 537 查看
在浏览器里,我们经常在网页上看到中意的内容,想选择特定的内容,完后复制,再在其它地方粘贴,这是很常见的功能,不过由于是在网页中,所以理论上来说,应该是用JavaScript或者其它类似的技术实现的,而不是Android,今天在看Marshmallow的文档时,意外发现新推出了Text Selection的功能,就学习了一下,自己写了个小例子,先看效果图:



功能很简单,就是上面一个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;
}
});
}
}

源码下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息