Android textview获取选中的内容

1.首先设置textview可选:
android:textIsSelectable="true"


2.为textview设置监听
textView.setCustomSelectionActionModeCallback(callback2);

3.这个时候我们只要重写ActionMode.Callback2接口(需要判断sdk版本是否大于23,小于使用callback接口

private ActionMode.Callback2 callback2 = new ActionMode.Callback2()
    {
        @Override
        public boolean onCreateActionMode(ActionMode actionMode, Menu menu)
        {
            MenuInflater inflater = actionMode.getMenuInflater();
            inflater.inflate(R.menu.text_selected, menu);
            return true;
        }

        @Override
        public boolean onPrepareActionMode(ActionMode actionMode, Menu menu)
        {// 此方法用于清楚系统自带的meneitem(例如复制分享)
//            MenuInflater menuInflater = actionMode.getMenuInflater();
//            menu.clear();
//            menuInflater.inflate(R.menu.text_selected,menu);
//            return true;
            return false;
        }

        @Override
        public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem)
        {
            switch (menuItem.getItemId())
            {
                case R.id.toast:
                {
           
                    Toast.makeText(MainActivity.this, "toast:" , Toast.LENGTH_SHORT).show();
                    break;
                }

                case R.id.finish:
                {
                    MainActivity.this.finish();
                    break;
                }

                default:return false;
            }
            return true;
        }

        @Override
        public void onDestroyActionMode(ActionMode actionMode)
        {

        }
    };

4.要想获取所选中的内容,从callback2接口中看只有menu和menuitem,无法获取,但系统自带的有复制和剪切的功能,这时候就看一看系统是如何实现的。从设置监听的方法开始,见下面源码;监听事件被交由Edit这和类的mCustomSelectionActionModeCallback属性。

public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) {
        createEditorIfNeeded();
        mEditor.mCustomSelectionActionModeCallback = actionModeCallback;
    }
再看Editor这个类,对 mCustomSelectionActionModeCallback属性操作的是getCustomCallback()方法,
private Callback getCustomCallback() {
            return mHasSelection
                    ? mCustomSelectionActionModeCallback
                    : mCustomInsertionActionModeCallback;
        }
再看有哪些地方用到这个方法: Editor的内部类TextActionModeCallback
private class TextActionModeCallback extends ActionMode.Callback2 {
        private final Path mSelectionPath = new Path();
        private final RectF mSelectionBounds = new RectF();
        private final boolean mHasSelection;

        private int mHandleHeight;

        public TextActionModeCallback(boolean hasSelection) {
            mHasSelection = hasSelection;
            if (mHasSelection) {
                SelectionModifierCursorController selectionController = getSelectionController();
                if (selectionController.mStartHandle == null) {
                    // As these are for initializing selectionController, hide() must be called.
                    selectionController.initDrawables();
                    selectionController.initHandles();
                    selectionController.hide();
                }
                mHandleHeight = Math.max(
                        mSelectHandleLeft.getMinimumHeight(),
                        mSelectHandleRight.getMinimumHeight());
            } else {
                InsertionPointCursorController insertionController = getInsertionController();
                if (insertionController != null) {
                    insertionController.getHandle();
                    mHandleHeight = mSelectHandleCenter.getMinimumHeight();
                }
            }
        }

        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            mode.setTitle(null);
            mode.setSubtitle(null);
            mode.setTitleOptionalHint(true);
            populateMenuWithItems(menu);

            Callback customCallback = getCustomCallback();
            if (customCallback != null) {
                if (!customCallback.onCreateActionMode(mode, menu)) {
                    // The custom mode can choose to cancel the action mode, dismiss selection.
                    Selection.setSelection((Spannable) mTextView.getText(),
                            mTextView.getSelectionEnd());
                    return false;
                }
            }

            if (mTextView.canProcessText()) {
                mProcessTextIntentActionsHandler.onInitializeMenu(menu);
            }

            if (menu.hasVisibleItems() || mode.getCustomView() != null) {
                mTextView.setHasTransientState(true);
                return true;
            } else {
                return false;
            }
        }

        private Callback getCustomCallback() {
            return mHasSelection
                    ? mCustomSelectionActionModeCallback
                    : mCustomInsertionActionModeCallback;
        }

        private void populateMenuWithItems(Menu menu) {
            if (mTextView.canCut()) {
                menu.add(Menu.NONE, TextView.ID_CUT, MENU_ITEM_ORDER_CUT,
                        com.android.internal.R.string.cut).
                    setAlphabeticShortcut('x').
                    setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
            }

            if (mTextView.canCopy()) {
                menu.add(Menu.NONE, TextView.ID_COPY, MENU_ITEM_ORDER_COPY,
                        com.android.internal.R.string.copy).
                    setAlphabeticShortcut('c').
                    setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
            }

            if (mTextView.canPaste()) {
                menu.add(Menu.NONE, TextView.ID_PASTE, MENU_ITEM_ORDER_PASTE,
                        com.android.internal.R.string.paste).
                    setAlphabeticShortcut('v').
                    setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
            }

            if (mTextView.canShare()) {
                menu.add(Menu.NONE, TextView.ID_SHARE, MENU_ITEM_ORDER_SHARE,
                        com.android.internal.R.string.share).
                    setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
            }

            updateSelectAllItem(menu);
            updateReplaceItem(menu);
        }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            updateSelectAllItem(menu);
            updateReplaceItem(menu);

            Callback customCallback = getCustomCallback();
            if (customCallback != null) {
                return customCallback.onPrepareActionMode(mode, menu);
            }
            return true;
        }

        private void updateSelectAllItem(Menu menu) {
            boolean canSelectAll = mTextView.canSelectAllText();
            boolean selectAllItemExists = menu.findItem(TextView.ID_SELECT_ALL) != null;
            if (canSelectAll && !selectAllItemExists) {
                menu.add(Menu.NONE, TextView.ID_SELECT_ALL, MENU_ITEM_ORDER_SELECT_ALL,
                        com.android.internal.R.string.selectAll)
                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
            } else if (!canSelectAll && selectAllItemExists) {
                menu.removeItem(TextView.ID_SELECT_ALL);
            }
        }

        private void updateReplaceItem(Menu menu) {
            boolean canReplace = mTextView.isSuggestionsEnabled() && shouldOfferToShowSuggestions()
                    && !(mTextView.isInExtractedMode() && mTextView.hasSelection());
            boolean replaceItemExists = menu.findItem(TextView.ID_REPLACE) != null;
            if (canReplace && !replaceItemExists) {
                menu.add(Menu.NONE, TextView.ID_REPLACE, MENU_ITEM_ORDER_REPLACE,
                        com.android.internal.R.string.replace)
                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
            } else if (!canReplace && replaceItemExists) {
                menu.removeItem(TextView.ID_REPLACE);
            }
        }

        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) {
                return true;
            }
            Callback customCallback = getCustomCallback();
            if (customCallback != null && customCallback.onActionItemClicked(mode, item)) {
                return true;
            }
            return mTextView.onTextContextMenuItem(item.getItemId());
        }

        @Override
        public void onDestroyActionMode(ActionMode mode) {
            Callback customCallback = getCustomCallback();
            if (customCallback != null) {
                customCallback.onDestroyActionMode(mode);
            }

            /*
             * If we're ending this mode because we're detaching from a window,
             * we still have selection state to preserve. Don't clear it, we'll
             * bring back the selection mode when (if) we get reattached.
             */
            if (!mPreserveDetachedSelection) {
                Selection.setSelection((Spannable) mTextView.getText(),
                        mTextView.getSelectionEnd());
                mTextView.setHasTransientState(false);
            }

            if (mSelectionModifierCursorController != null) {
                mSelectionModifierCursorController.hide();
            }

            mTextActionMode = null;
        }

        @Override
        public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
            if (!view.equals(mTextView) || mTextView.getLayout() == null) {
                super.onGetContentRect(mode, view, outRect);
                return;
            }
            if (mTextView.getSelectionStart() != mTextView.getSelectionEnd()) {
                // We have a selection.
                mSelectionPath.reset();
                mTextView.getLayout().getSelectionPath(
                        mTextView.getSelectionStart(), mTextView.getSelectionEnd(), mSelectionPath);
                mSelectionPath.computeBounds(mSelectionBounds, true);
                mSelectionBounds.bottom += mHandleHeight;
            } else if (mCursorCount == 2) {
                // We have a split cursor. In this case, we take the rectangle that includes both
                // parts of the cursor to ensure we don't obscure either of them.
                Rect firstCursorBounds = mCursorDrawable[0].getBounds();
                Rect secondCursorBounds = mCursorDrawable[1].getBounds();
                mSelectionBounds.set(
                        Math.min(firstCursorBounds.left, secondCursorBounds.left),
                        Math.min(firstCursorBounds.top, secondCursorBounds.top),
                        Math.max(firstCursorBounds.right, secondCursorBounds.right),
                        Math.max(firstCursorBounds.bottom, secondCursorBounds.bottom)
                                + mHandleHeight);
            } else {
                // We have a single cursor.
                Layout layout = getActiveLayout();
                int line = layout.getLineForOffset(mTextView.getSelectionStart());
                float primaryHorizontal =
                        layout.getPrimaryHorizontal(mTextView.getSelectionStart());
                mSelectionBounds.set(
                        primaryHorizontal,
                        layout.getLineTop(line),
                        primaryHorizontal,
                        layout.getLineTop(line + 1) + mHandleHeight);
            }
            // Take TextView's padding and scroll into account.
            int textHorizontalOffset = mTextView.viewportToContentHorizontalOffset();
            int textVerticalOffset = mTextView.viewportToContentVerticalOffset();
            outRect.set(
                    (int) Math.floor(mSelectionBounds.left + textHorizontalOffset),
                    (int) Math.floor(mSelectionBounds.top + textVerticalOffset),
                    (int) Math.ceil(mSelectionBounds.right + textHorizontalOffset),
                    (int) Math.ceil(mSelectionBounds.bottom + textVerticalOffset));
        }
    }
这个时候看到这个类是继承的callback2接口,该类是系统默认的监听类,此时我们需要找的是点击系统弹出的复制的点击执行哪些操作,所以我们重点看onActionItemClicked()方法

@Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) {
                return true;
            }
            Callback customCallback = getCustomCallback();
            if (customCallback != null && customCallback.onActionItemClicked(mode, item)) {
                return true;
            }
            return mTextView.onTextContextMenuItem(item.getItemId());
        }
此时可以看到系统默认执行的mTextView.onTextContextMenuItem(item.getItemId()),这时我们进入这个方法
public boolean onTextContextMenuItem(int id) {
        int min = 0;
        int max = mText.length();

        if (isFocused()) {
            final int selStart = getSelectionStart();
            final int selEnd = getSelectionEnd();

            min = Math.max(0, Math.min(selStart, selEnd));
            max = Math.max(0, Math.max(selStart, selEnd));
        }.......
}
这个方法只需要看前半部分,min和max这两个值,这两个是截取的字符串所在原textview的text的位置,我们在点击我们的menu时获取这两个值,最后吐丝出来,源码见下:

import android.annotation.TargetApi;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity
{
    private TextView textView;
    private ActionMode.Callback2 callback2 = new ActionMode.Callback2()
    {
        @Override
        public boolean onCreateActionMode(ActionMode actionMode, Menu menu)
        {
            MenuInflater inflater = actionMode.getMenuInflater();
            inflater.inflate(R.menu.text_selected, menu);
            return true;
        }

        @Override
        public boolean onPrepareActionMode(ActionMode actionMode, Menu menu)
        {
//            MenuInflater menuInflater = actionMode.getMenuInflater();
//            menu.clear();
//            menuInflater.inflate(R.menu.text_selected,menu);
//            return true;
            return false;
        }

        @Override
        public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem)
        {
            switch (menuItem.getItemId())
            {
                case R.id.toast:
                {
                    if (textView == null) return false;
                    int min = 0;
                    int max = textView.length();
                    if (textView.isFocused()) {
                        final int selStart = textView.getSelectionStart();
                        final int selEnd = textView.getSelectionEnd();

                        min = Math.max(0, Math.min(selStart, selEnd));
                        max = Math.max(0, Math.max(selStart, selEnd));
                    }

                    String content = String.valueOf(textView.getText().subSequence(min, max)) ;

                    Toast.makeText(MainActivity.this, "toast:" + content, Toast.LENGTH_SHORT).show();
                    break;
                }

                case R.id.finish:
                {
                    MainActivity.this.finish();
                    break;
                }

                default:return false;
            }
            return true;
        }

        @Override
        public void onDestroyActionMode(ActionMode actionMode)
        {

        }
    };

    @TargetApi(Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView)findViewById(R.id.text);
        textView.setCustomSelectionActionModeCallback(callback2);

    }
}
menu文件:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/toast"
        android:title="吐丝"
        />

    <item
        android:id="@+id/finish"
        android:title="finish"
        />
</menu>






版权声明:本文为abchuge原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。