Как переопределить выбор текста по умолчанию для Android WebView OS 4.1+?

Перед тем, как опубликовать этот вопрос, я много искал, но не смог найти четких ответов по этому поводу.

Мне нужно переопределить выбор текста по умолчанию для веб-просмотра Android и показать параметры диалогового окна выбора настраиваемого текста. Я пробовал этот пример кода project.

Этот образец проекта работает на следующих устройствах и эмуляторах:

  • Планшет Acer Iconia a500: 10 дюймов: ОС Android - 3.0
  • Планшет Acer Iconia a500: 10 дюймов: ОС Android - 3,2
  • Samsung Galaxy Tab: 10 дюймов: ОС Android - 4.0
  • Samsung Galaxy Tab: 7 дюймов: ОС Android - 4.0
  • Эмулятор: Skin-WVGA800: ОС Android - 4.1.2

Не работает на следующих устройствах:

  • Samsung Galaxy Tab: 10 дюймов: ОС Android - 4.1.2
  • Samsung Galaxy Tab: 7 дюймов: ОС Android - 4.1.2

В ОС Android версии 4.1 и 4.1+ вместо отображения моего диалогового окна выбора настраиваемого текста отображается панель действий системы Android по умолчанию для выбора текста.

Я много искал по этому поводу, многие предлагали использовать метод onLongClick () для интерфейс

Я уже задавал вопрос на этом форуме, см. Ссылку < / a>, с ответами на эти вопросы я могу клонировать событие onLongClick (), но не могу остановить панель действий для выбора текста по умолчанию.

По этому сценарию у меня есть несколько вопросов.

1.Почему метод onLongClick () перестает работать на устройстве под управлением ОС Android версии 4.1+?

2.Как остановить панель действий выделения текста по умолчанию при долгом нажатии на текст из веб-просмотра?

Это мой собственный класс веб-просмотра.

  package com.epubreader.ebook;
  import org.json.JSONException;
  import org.json.JSONObject;
  import android.app.Activity;
  import android.content.Context;
  import android.graphics.Rect;
  import android.graphics.Region;
  import android.os.Handler;
  import android.os.Message;
  import android.util.AttributeSet;
  import android.util.DisplayMetrics;
  import android.util.Log;
  import android.view.ContextMenu;
  import android.view.Display;
  import android.view.GestureDetector;
  import android.view.LayoutInflater;
  import android.view.MotionEvent;
  import android.view.View;
  import android.view.View.OnLongClickListener;
  import android.view.View.OnTouchListener;
  import android.view.ViewGroup;
  import android.view.WindowManager;
  import android.webkit.WebView;
  import android.widget.ImageView;
  import android.widget.LinearLayout;
  import android.widget.Toast;

  import com.epubreader.R;
  import com.epubreader.drag.DragController;
  import com.epubreader.drag.DragLayer;
  import com.epubreader.drag.DragListener;
  import com.epubreader.drag.DragSource;
  import com.epubreader.drag.MyAbsoluteLayout;
  import com.epubreader.menu.menuAnimationHelper;
  import com.epubreader.textselection.WebTextSelectionJSInterface;
  import com.epubreader.textselectionoverlay.ActionItem;
  import com.epubreader.textselectionoverlay.QuickAction;
  import com.epubreader.textselectionoverlay.QuickAction.OnDismissListener;



public class CustomWebView extends WebView implements WebTextSelectionJSInterface,
OnTouchListener , OnLongClickListener, OnDismissListener, DragListener{

/** The logging tag. */
private static final String TAG = "CustomWebView";

/** Context. */
protected   Context ctx;

/** The context menu. */
private QuickAction mContextMenu;

/** The drag layer for selection. */
private DragLayer mSelectionDragLayer;

/** The drag controller for selection. */
private DragController mDragController;

/** The start selection handle. */
private ImageView mStartSelectionHandle;

/** the end selection handle. */
private ImageView mEndSelectionHandle;

/** The selection bounds. */
private Rect mSelectionBounds = null;

/** The previously selected region. */
protected Region lastSelectedRegion = null;

/** The selected range. */
protected String selectedRange = "";

/** The selected text. */
protected String selectedText = "";

/** Javascript interface for catching text selection. */


/** Selection mode flag. */
protected boolean inSelectionMode = false;

/** Flag to stop from showing context menu twice. */
protected boolean contextMenuVisible = false;

/** The current content width. */
protected int contentWidth = 0;


/** Identifier for the selection start handle. */
private final int SELECTION_START_HANDLE = 0;

/** Identifier for the selection end handle. */
private final int SELECTION_END_HANDLE = 1;

/** Last touched selection handle. */
private int mLastTouchedSelectionHandle = -1;

/** Variables for Left & Right Menu ***/
private View menuView;
private LinearLayout toiLay;
private menuAnimationHelper _menuAnimationHelper;
private TocTranslateAnimation _tocTranslateAnimation;

private CustomWebView _customWebView;



public CustomWebView(Context context) {
    super(context);

    this.ctx = context;
    this.setup(context);
}

public CustomWebView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    this.ctx = context;
    this.setup(context);

}

public CustomWebView(Context context, AttributeSet attrs) {
    super(context, attrs);

    this.ctx = context;
    this.setup(context);

}


//*****************************************************
//*
//*     Touch Listeners
//*
//*****************************************************

private boolean mScrolling = false;
private float mScrollDiffY = 0;
private float mLastTouchY = 0;
private float mScrollDiffX = 0;
private float mLastTouchX = 0;

@Override
public boolean onTouch(View v, MotionEvent event) {


    float xPoint = getDensityIndependentValue(event.getX(), ctx) / getDensityIndependentValue(this.getScale(), ctx);
    float yPoint = getDensityIndependentValue(event.getY(), ctx) / getDensityIndependentValue(this.getScale(), ctx);

    // TODO: Need to update this to use this.getScale() as a factor.

    //Log.d(TAG, "onTouch " + xPoint + " , " + yPoint);

    closeMenu();
   if(event.getAction() == MotionEvent.ACTION_DOWN){

        final String startTouchUrl = String.format("javascript:android.selection.startTouch(%f, %f);", 
                xPoint, yPoint);

        mLastTouchX = xPoint;
        mLastTouchY = yPoint;

        ((Activity)this.ctx).runOnUiThread(new Runnable() {

            @Override
            public void run() {
                loadUrl(startTouchUrl);
            }
        });

         // This two line clones the onLongClick()
         longClickHandler.removeCallbacks(longClickRunnable);
         longClickHandler.postDelayed(longClickRunnable,300);


    }
    else if(event.getAction() == MotionEvent.ACTION_UP){
        // Check for scrolling flag
        if(!mScrolling){
            this.endSelectionMode();
        }

         // This line clones the onLongClick()
        longClickHandler.removeCallbacks(longClickRunnable);

        mScrollDiffX = 0;
        mScrollDiffY = 0;
        mScrolling = false;



    }else if(event.getAction() == MotionEvent.ACTION_MOVE){

        mScrollDiffX += (xPoint - mLastTouchX);
        mScrollDiffY += (yPoint - mLastTouchY);

        mLastTouchX = xPoint;
        mLastTouchY = yPoint;


        // Only account for legitimate movement.
        if(Math.abs(mScrollDiffX) > 10 || Math.abs(mScrollDiffY) > 10){
            mScrolling = true;

        }
         // This line clones the onLongClick()
        longClickHandler.removeCallbacks(longClickRunnable);

    }

    // If this is in selection mode, then nothing else should handle this touch
    return false;
}

/**
 * Pass References of Left & Right Menu
 */

public void initMenu(LinearLayout _toiLay,View _menuView,menuAnimationHelper menuAnimationHelper,
        TocTranslateAnimation tocTranslateAnimation){
    toiLay = _toiLay;
    menuView = _menuView;
    _menuAnimationHelper = menuAnimationHelper;
    _tocTranslateAnimation = tocTranslateAnimation;
}

private void closeMenu(){

    if(_menuAnimationHelper != null && _menuAnimationHelper.isMenuOpenBool){
        _menuAnimationHelper.close(menuView);
        _menuAnimationHelper.isMenuOpenBool = false;
    }

    if(_tocTranslateAnimation != null && _tocTranslateAnimation.isTocListOpenBool){
        _tocTranslateAnimation.close(toiLay);
        _tocTranslateAnimation.isTocListOpenBool = false;
    }
}

      public void removeOverlay(){
    Log.d("JsHandler", "in java removeOverlay" + mScrolling);


    this.endSelectionMode();
    mScrollDiffX = 0;
    mScrollDiffY = 0;
    mScrolling = false;
}

@Override 
public boolean onLongClick(View v){

    Log.d(TAG, "from webView onLongClick ");
    mScrolling = true;
    ((Activity)this.ctx).runOnUiThread(new Runnable() {

        @Override
        public void run() {
            loadUrl("javascript:android.selection.longTouch()");
        }
    });

    Toast.makeText(ctx, "Long click is clicked ", Toast.LENGTH_LONG).show();
    // Don't let the webview handle it
    return true;
}





//*****************************************************
//*
//*     Setup
//*
//*****************************************************

ContextMenu.ContextMenuInfo contextMenuInfo;

/**
 * Setups up the web view.
 * @param context
 */
protected void setup(Context context){


    // On Touch Listener

    this.setOnTouchListener(this);

    this.setClickable(false);
    this.setLongClickable(true);
    this.setOnLongClickListener(this);

    contextMenuInfo = this.getContextMenuInfo();
    // Webview setup
    this.getSettings().setJavaScriptEnabled(true);
    this.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);

    // Create the selection handles
    createSelectionLayer(context);

    // Set to the empty region
    Region region = new Region();
    region.setEmpty();

    _customWebView = this;

    this.lastSelectedRegion = region;


}

/**
 * To clone OnLongClick Listener because its not responding for version 4.1
 */
public Runnable longClickRunnable = new Runnable() {
    public void run() {
        longClickHandler.sendEmptyMessage(0);

    }
};


public Handler longClickHandler = new Handler(){

    public void handleMessage(Message m){
        _customWebView.loadUrl("javascript:android.selection.longTouch();");
        mScrolling = true;
    }
};


public WebTextSelectionJSInterface getTextSelectionJsInterface(){
    return this;
}

//*****************************************************
//*
//*     Selection Layer Handling
//*
//*****************************************************

/**
 * Creates the selection layer.
 * 
 * @param context
 */
protected void createSelectionLayer(Context context){

    LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    this.mSelectionDragLayer = (DragLayer) inflater.inflate(R.layout.selection_drag_layer, null);


    // Make sure it's filling parent
    this.mDragController = new DragController(context);
    this.mDragController.setDragListener(this);
    this.mDragController.addDropTarget(mSelectionDragLayer);
    this.mSelectionDragLayer.setDragController(mDragController);


    this.mStartSelectionHandle = (ImageView) this.mSelectionDragLayer.findViewById(R.id.startHandle);
    this.mStartSelectionHandle.setTag(new Integer(SELECTION_START_HANDLE));
    this.mEndSelectionHandle = (ImageView) this.mSelectionDragLayer.findViewById(R.id.endHandle);
    this.mEndSelectionHandle.setTag(new Integer(SELECTION_END_HANDLE));

    OnTouchListener handleTouchListener = new OnTouchListener(){

        @Override
        public boolean onTouch(View v, MotionEvent event) {

            boolean handledHere = false;
            final int action = event.getAction();
            // Down event starts drag for handle.
            if (action == MotionEvent.ACTION_DOWN) {
               handledHere = startDrag (v);
               mLastTouchedSelectionHandle = (Integer) v.getTag();
            }
            return handledHere;
        }
    };

    this.mStartSelectionHandle.setOnTouchListener(handleTouchListener);
    this.mEndSelectionHandle.setOnTouchListener(handleTouchListener);


}

/**
 * Starts selection mode on the UI thread
 */
private Handler startSelectionModeHandler = new Handler(){

    public void handleMessage(Message m){

        if(mSelectionBounds == null)
            return;

        addView(mSelectionDragLayer);

        drawSelectionHandles();


        int contentHeight = (int) Math.ceil(getDensityDependentValue(getContentHeight(), ctx));

        // Update Layout Params
        ViewGroup.LayoutParams layerParams = mSelectionDragLayer.getLayoutParams();
        layerParams.height = contentHeight;
        layerParams.width = contentWidth;
        mSelectionDragLayer.setLayoutParams(layerParams);

    }

};

/**
 * Starts selection mode.
 * 
 * @param   selectionBounds
 */
public void startSelectionMode(){

    this.startSelectionModeHandler.sendEmptyMessage(0);

}

// Ends selection mode on the UI thread
private Handler endSelectionModeHandler = new Handler(){
    public void handleMessage(Message m){

        //Log.d("TableContentsWithDisplay", "in endSelectionModeHandler");

        removeView(mSelectionDragLayer);
        if(getParent() != null && mContextMenu != null && contextMenuVisible){
            // This will throw an error if the webview is being redrawn.
            // No error handling needed, just need to stop the crash.
            try{
                mContextMenu.dismiss();
            }
            catch(Exception e){

            }
        }
        mSelectionBounds = null;
        mLastTouchedSelectionHandle = -1;
        try {
            ((Activity)ctx).runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    loadUrl("javascript: android.selection.clearSelection();");
                }
            });

        } catch (Exception e) {
            // TODO: handle exception
        }

    }
};

/**
 * Ends selection mode.
 */
public void endSelectionMode(){

    this.endSelectionModeHandler.sendEmptyMessage(0);

}

/**
 * Calls the handler for drawing the selection handles.
 */
private void drawSelectionHandles(){
    this.drawSelectionHandlesHandler.sendEmptyMessage(0);
}

/**
 * Handler for drawing the selection handles on the UI thread.
 */
private Handler drawSelectionHandlesHandler = new Handler(){
    public void handleMessage(Message m){

        MyAbsoluteLayout.LayoutParams startParams = (com.epubreader.drag.MyAbsoluteLayout.LayoutParams) mStartSelectionHandle.getLayoutParams();
        startParams.x = (int) (mSelectionBounds.left - mStartSelectionHandle.getDrawable().getIntrinsicWidth());
        startParams.y = (int) (mSelectionBounds.top - mStartSelectionHandle.getDrawable().getIntrinsicHeight());

        // Stay on screen.
        startParams.x = (startParams.x < 0) ? 0 : startParams.x;
        startParams.y = (startParams.y < 0) ? 0 : startParams.y;


        mStartSelectionHandle.setLayoutParams(startParams);

        MyAbsoluteLayout.LayoutParams endParams = (com.epubreader.drag.MyAbsoluteLayout.LayoutParams) mEndSelectionHandle.getLayoutParams();
        endParams.x = (int) mSelectionBounds.right;
        endParams.y = (int) mSelectionBounds.bottom;
        endParams.x = (endParams.x < 0) ? 0 : endParams.x;
        endParams.y = (endParams.y < 0) ? 0 : endParams.y;

        mEndSelectionHandle.setLayoutParams(endParams);

    }
};

/**
 * Checks to see if this view is in selection mode.
 * @return
 */
public boolean isInSelectionMode(){

    return this.mSelectionDragLayer.getParent() != null;


}

//*****************************************************
//*
//*     DragListener Methods
//*
//*****************************************************

/**
 * Start dragging a view.
 *
 */    
private boolean startDrag (View v)
{
    // Let the DragController initiate a drag-drop sequence.
    // I use the dragInfo to pass along the object being dragged.
    // I'm not sure how the Launcher designers do this.
    Object dragInfo = v;
    mDragController.startDrag (v, mSelectionDragLayer, dragInfo, DragController.DRAG_ACTION_MOVE);
    return true;
}


@Override
public void onDragStart(DragSource source, Object info, int dragAction) {
    // TODO Auto-generated method stub

}

@Override@SuppressWarnings("deprecation")
public void onDragEnd() {
    // TODO Auto-generated method stub

    MyAbsoluteLayout.LayoutParams startHandleParams = (MyAbsoluteLayout.LayoutParams) this.mStartSelectionHandle.getLayoutParams();
    MyAbsoluteLayout.LayoutParams endHandleParams = (MyAbsoluteLayout.LayoutParams) this.mEndSelectionHandle.getLayoutParams();

    float scale = getDensityIndependentValue(this.getScale(), ctx);

    float startX = startHandleParams.x - this.getScrollX();
    float startY = startHandleParams.y - this.getScrollY();
    float endX = endHandleParams.x - this.getScrollX();
    float endY = endHandleParams.y - this.getScrollY();

    startX = getDensityIndependentValue(startX, ctx) / scale;
    startY = getDensityIndependentValue(startY, ctx) / scale;
    endX = getDensityIndependentValue(endX, ctx) / scale;
    endY = getDensityIndependentValue(endY, ctx) / scale;


    if(mLastTouchedSelectionHandle == SELECTION_START_HANDLE && startX > 0 && startY > 0){
        final String saveStartString = String.format("javascript: android.selection.setStartPos(%f, %f);", startX, startY);

        ((Activity)ctx).runOnUiThread(new Runnable() {

            @Override
            public void run() {
                loadUrl(saveStartString);
            }
        });

    }


    if(mLastTouchedSelectionHandle == SELECTION_END_HANDLE && endX > 0 && endY > 0){
        final String saveEndString = String.format("javascript: android.selection.setEndPos(%f, %f);", endX, endY);

        ((Activity)ctx).runOnUiThread(new Runnable() {

            @Override
            public void run() {
                loadUrl(saveEndString);
            }
        });

    }



}


//*****************************************************
//*
//*     Context Menu Creation
//*
//*****************************************************

/**
 * Shows the context menu using the given region as an anchor point.
 * @param region
 */
private void showContextMenu(Rect displayRect){

    // Don't show this twice
    if(this.contextMenuVisible){
        return;
    }

    // Don't use empty rect
    //if(displayRect.isEmpty()){
    if(displayRect.right <= displayRect.left){
        return;
    }

    //Copy action item
    ActionItem buttonOne = new ActionItem();

    buttonOne.setTitle("HighLight");
    buttonOne.setActionId(1);
    //buttonOne.setIcon(getResources().getDrawable(R.drawable.menu_search));


    //Highlight action item
    ActionItem buttonTwo = new ActionItem();

    buttonTwo.setTitle("Note");
    buttonTwo.setActionId(2);
    //buttonTwo.setIcon(getResources().getDrawable(R.drawable.menu_info));

    ActionItem buttonThree = new ActionItem();

    buttonThree.setTitle("Help");
    buttonThree.setActionId(3);
    //buttonThree.setIcon(getResources().getDrawable(R.drawable.menu_eraser));



    // The action menu
    mContextMenu  = new QuickAction(this.getContext());
    mContextMenu.setOnDismissListener(this);

    // Add buttons
    mContextMenu.addActionItem(buttonOne);

    mContextMenu.addActionItem(buttonTwo);

    mContextMenu.addActionItem(buttonThree);



    //setup the action item click listener
    mContextMenu.setOnActionItemClickListener(new QuickAction.OnActionItemClickListener() {

        @Override
        public void onItemClick(QuickAction source, int pos,
            int actionId) {

            if (actionId == 1) { 

                callHighLight();
            } 
            else if (actionId == 2) { 

                callNote();
            } 
            else if (actionId == 3) { 
                // Do Button 3 stuff
                Log.i(TAG, "Hit Button 3");
            }

            contextMenuVisible = false;

        }

    });

    this.contextMenuVisible = true;
    mContextMenu.show(this, displayRect);
}

private void callHighLight(){

    ((Activity)this.ctx).runOnUiThread(new Runnable() {

        @Override
        public void run() {
            loadUrl("javascript:init_txt_selection_event()");
            loadUrl("javascript:highlightme_("+0+")");
        }
    });

}

private void callNote(){

    ((Activity)this.ctx).runOnUiThread(new Runnable() {

        @Override
        public void run() {
            loadUrl("javascript:init_txt_selection_event()");
            loadUrl("javascript:fnGetUserAddedNote('1')");
        }
    });

}


//*****************************************************
//*
//*     OnDismiss Listener
//*
//*****************************************************
/**
 * Clears the selection when the context menu is dismissed.
 */
public void onDismiss(){
    //clearSelection();
    this.contextMenuVisible = false;
}

//*****************************************************
//*
//*     Text Selection Javascript Interface Listener
//*
//*****************************************************
/**
 * The user has started dragging the selection handles.
 */
public void tsjiStartSelectionMode(){

    this.startSelectionMode();


}

/**
 * The user has stopped dragging the selection handles.
 */
public void tsjiEndSelectionMode(){

    this.endSelectionMode();
}

/**
 * The selection has changed
 * @param range
 * @param text
 * @param handleBounds
 * @param menuBounds
 * @param showHighlight
 * @param showUnHighlight
 */@SuppressWarnings("deprecation")
public void tsjiSelectionChanged(String range, String text, String handleBounds, String menuBounds){
    try {

        //Log.d(TAG, "tsjiSelectionChanged :- handleBounds " + handleBounds);
        JSONObject selectionBoundsObject = new JSONObject(handleBounds);

        float scale = getDensityIndependentValue(this.getScale(), ctx);

        Rect handleRect = new Rect();
        handleRect.left = (int) (getDensityDependentValue(selectionBoundsObject.getInt("left"), getContext()) * scale);
        handleRect.top = (int) (getDensityDependentValue(selectionBoundsObject.getInt("top"), getContext()) * scale);
        handleRect.right = (int) (getDensityDependentValue(selectionBoundsObject.getInt("right"), getContext()) * scale);
        handleRect.bottom = (int) (getDensityDependentValue(selectionBoundsObject.getInt("bottom"), getContext()) * scale);

        this.mSelectionBounds = handleRect;
        this.selectedRange = range;
        this.selectedText = text;

        JSONObject menuBoundsObject = new JSONObject(menuBounds);

        Rect displayRect = new Rect();
        displayRect.left = (int) (getDensityDependentValue(menuBoundsObject.getInt("left"), getContext()) * scale);
        displayRect.top = (int) (getDensityDependentValue(menuBoundsObject.getInt("top") - 25, getContext()) * scale);
        displayRect.right = (int) (getDensityDependentValue(menuBoundsObject.getInt("right"), getContext()) * scale);
        displayRect.bottom = (int) (getDensityDependentValue(menuBoundsObject.getInt("bottom") + 25, getContext()) * scale);

        if(!this.isInSelectionMode()){
            this.startSelectionMode();
        }

        // This will send the menu rect
        this.showContextMenu(displayRect);

        drawSelectionHandles();


    } catch (JSONException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }


}


/**
 * Receives the content width for the page.
 */
public void tsjiSetContentWidth(float contentWidth){
    this.contentWidth = (int) this.getDensityDependentValue(contentWidth, ctx);
}


//*****************************************************
//*
//*     Density Conversion
//*
//*****************************************************
/**
 * Returns the density dependent value of the given float
 * @param val
 * @param ctx
 * @return
 */
public float getDensityDependentValue(float val, Context ctx){
    // Get display from context
    Display display = ((WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
    // Calculate min bound based on metrics
    DisplayMetrics metrics = new DisplayMetrics();
    display.getMetrics(metrics);
    return val * (metrics.densityDpi / 160f);
}

/**
 * Returns the density independent value of the given float
 * @param val
 * @param ctx
 * @return
 */
public float getDensityIndependentValue(float val, Context ctx){

    // Get display from context
    Display display = ((WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
    // Calculate min bound based on metrics
    DisplayMetrics metrics = new DisplayMetrics();
    display.getMetrics(metrics);
    return val / (metrics.densityDpi / 160f);
}

}

Заранее спасибо!


person sachin003    schedule 10.06.2013    source источник
comment
Не могли бы вы опубликовать, как вы реализовали onLongClick ()? На моем GalaxyS3 (OS 4.1.2) он работает без проблем, и я успешно переопределил его, поэтому я не показываю режим действия.   -  person gunar    schedule 14.06.2013
comment
@gunar Я отредактировал свой вопрос, добавив собственный код веб-просмотра, этот код имеет как метод onLongClick, так и клон onLongClick с использованием longClickRunnable, longClickHandler, остальной код и вспомогательные классы для Drag & Drop - это тот же образец проекта из github.com/btate/BTAndroidWebViewSelection   -  person sachin003    schedule 14.06.2013
comment
@gunar Я тестировал тот же код на GalaxyS3 OS 4.1.2, он работает хорошо, предотвращает режим выбора текста в веб-просмотре и показывает мои параметры пользовательского выбора текста по умолчанию, но тот же код не работает на 10-дюймовом планшете Samsung с ОС 4.1.2   -  person sachin003    schedule 14.06.2013
comment
Попробуйте исследовать API WebView во время выполнения с помощью отражения. Планшеты Samsung имеют немного другую реализацию WebView. Были некоторые специальные методы, которые могут вам помочь. Точных имен и подписей не помню, попробую сделать позже.   -  person Oleksii K.    schedule 19.06.2013
comment
Привет, Сачин, я столкнулся с той же проблемой при реализации richtexteditor с использованием веб-просмотра. Просто интересно, можете ли вы найти обходной путь для этого .... спасибо   -  person Dev.Sinto    schedule 19.08.2013
comment
Пришлось сделать нечто подобное, вот как я этого добился. stackoverflow.com/a/21626675/3248932   -  person Pulkit Gupta    schedule 07.02.2014


Ответы (9)


Я понимаю, что это старый вопрос, но на него нет принятого ответа, и ни один из предоставленных ответов не получил большого количества голосов.

Я добился того, чего вы пытаетесь достичь, создав обратный вызов режима настраиваемых действий. Это позволяет создавать настраиваемую контекстную панель действий (CAB), когда (в случае WebView) пользователь долго щелкает по представлению. ПРИМЕЧАНИЕ. Это работает только в 4.0+, а одна часть работает только в 4.4.

Создайте новый XML-файл Android, содержащий макет для вашего пользовательского меню. Вот мой (context_menu.xml) в качестве примера:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/copy"
        android:icon="@drawable/ic_action_copy"
        android:showAsAction="always" <!-- Forces this button to be shown -->
        android:title="@string/copy">
    </item>
    <item
        android:id="@+id/button2"
        android:icon="@drawable/menu_button2icon"
        android:showAsAction="ifRoom" <!-- Show if there is room on the screen-->
        android:title="@string/button2">
    </item>
    <!-- Add as many more items as you want.
         Note that if you use "ifRoom", an overflow menu will populate
         to hold the action items that did not fit in the action bar. -->

</menu>

Теперь, когда меню определено, создайте для него обратный вызов:

public class CustomWebView extends WebView {

    private ActionMode.Callback mActionModeCallback;

    private class CustomActionModeCallback implements ActionMode.Callback {

        // Called when the action mode is created; startActionMode() was called
        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            // Inflate a menu resource providing context menu items
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.context_menu, menu);
            return true;
        }

        // Called each time the action mode is shown.
        // Always called after onCreateActionMode, but
        // may be called multiple times if the mode is invalidated.
        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            // Note: This is called every time the selection handlebars move.
            return false; // Return false if nothing is done
        }

        // Called when the user selects a contextual menu item
        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {

            switch (item.getItemId()) {
                case R.id.copy:
                // Do some stuff for this button
                   mode.finish(); // Action picked, so close the CAB
                   return true;
                case R.id.button2:
                // Do some stuff for this button
                   mode.finish();
                   return true;
                // Create a case for every item
                ...
                default:
                   mode.finish();
                   return false;
            }
        }

        // Called when the user exits the action mode
        @Override
        public void onDestroyActionMode(ActionMode mode) {
            // This will clear the selection highlight and handlebars.
            // However, it only works in 4.4; I am still investigating
            // how to reliably clear the selection in 4.3 and earlier
            clearFocus();
        }
}

Как только эти части будут на месте, переопределите метод startActionMode, чтобы обратный вызов действительно выполнялся при долгом щелчке:

public class CustomWebView extends WebView {
    @Override
    public ActionMode startActionMode(Callback callback) {
        ViewParent parent = getParent();
        if (parent == null) {
        return null;
        }
        mActionModeCallback = new CustomActionModeCallback();
        return parent.startActionModeForChild(this, mActionModeCallback);
    }
}

Теперь у вас есть настраиваемое меню, которое будет отображаться и заполняться, когда пользователь долго нажимает на ваш WebView. Для справки я нашел эту информацию в руководстве для разработчиков Android. и изменил предложение из этот ответ.

И последнее замечание: этот метод отменяет собственное меню копирования / вставки для выделенного текста. Если вы хотите сохранить эту функциональность, вам нужно будет реализовать ее самостоятельно. (Вот почему моя первая кнопка - «копировать».) Если вам это нужно, обратитесь к это руководство Google для получения дополнительной информации и правильного способа его реализации.

person Sean Beach    schedule 21.03.2014
comment
Отличное решение! Через несколько дней я нашел твой пост. Спасибо +1 - person a.black13; 31.05.2014
comment
Я сделал аналогичное решение, но с одной проблемой: у меня есть 7 пользовательских элементов, но 3 из них не видны, потому что они находятся вне поля зрения. Можно ли прокрутить в меню выделения текста? @SciJoker - person ; 17.12.2015
comment
Как открыть настраиваемое всплывающее окно, если пользователь копирует текст из веб-просмотра? пожалуйста, помогите мне в этом :) - person Anand Savjani; 27.08.2016
comment
Работает ли это решение для Android 5 и выше. Я пытался реализовать его в пользовательском веб-просмотре, панель действий отображается, но щелчок по элементу не фиксируется. Кто-нибудь знает, что мне не хватает? - person coder; 08.08.2017

Можно использовать setOnTouchListener () для длительного / короткого длительного нажатия. и верните true, чтобы ничего не произошло, тем самым отменяя функцию выделения текста по умолчанию.

person user2783434    schedule 16.09.2013

Я смог решить эту проблему. Я тоже столкнулся с этой проблемой и не смог найти решения в Интернете.

Итак, если вы настроите прослушиватель LongClick, веб-просмотр вообще перестанет отображать выбор. Углубившись в код Webview, я обнаружил, что он вызывает метод startRunMode WebView и передает экземпляр класса SelectActionCallbackMode.

Я просто расширил класс Webview и переопределил метод startRunMode следующим образом:

public ActionMode startActionMode(ActionMode.Callback callback) 
{
    actionModeCallback = new CustomizedSelectActionModeCallback();
    return super.startActionMode(actionModeCallback);
}

Это заставило Webview отображать мой обратный вызов вместо отображения Webview по умолчанию. Это гарантировало, что выбор работал так же плавно, как и раньше, и моя CAB отображалась каждый раз, когда выбор был сделан. Единственная оговорка заключалась в том, что мне пришлось самому написать код, чтобы отключить CAB.

Проверено на устройствах 4.1, 4.2 и 4.3.

Надеюсь это поможет.

person Pulkit Gupta    schedule 07.02.2014

Я мог бы предложить обходной путь для этого. Вы можете использовать setOnTouchListener, а затем самостоятельно реализовать долгое нажатие.

onTouch --

 case MotionEvent.ACTION_DOWN:  
                    mHanlder.removeCallbacks(startActionBar);
                    mHandler.postDelayed(startActionBar,1000);

Таким образом можно добиться того же действия.

person blganesh101    schedule 27.06.2013
comment
Эта часть в порядке, но она также показывает панель контекстных действий по умолчанию (CAB) и изменяет размер веб-просмотра, в моем случае я хочу избежать CAB по умолчанию. - person sachin003; 31.07.2013
comment
Хорошо отключите долгий щелчок для веб-просмотра. this.setLongClickable(false); - person blganesh101; 31.07.2013
comment
@ blganesh101, вероятно, он работает на большинстве устройств, но не на Samsung Galaxy Tab на Android 4.1.2, как указано в вопросе - person Guillaume; 29.08.2013

по какой-то причине KeyEvent down & up не работает в API LEVEL 16+ (Android 4.1+ JELLY_BEAN). Он не запускает loadUrl WebView. Поэтому мне пришлось заменить dispatchKeyEvent на dispatchTouchEvent. Вот код:

...
MotionEvent touchDown = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, touchX, touchY, 0);
webView.dispatchTouchEvent(touchDown);
touchDown.recycle();

MotionEvent touchUp = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, touchX, touchY, 0);
webView.dispatchTouchEvent(touchUp);
touchUp.recycle();

String url = mUrl;
...

попробуй..

person Khunt Nirav    schedule 16.07.2013

Чтобы отключить выделение текста из веб-просмотра, попробуйте это,

webView.setWebChromeClient(new WebChromeClient(){

    [.... other overrides....]

    // @Override
    // https://bugzilla.wikimedia.org/show_bug.cgi?id=31484
    // If you DO NOT want to start selection by long click,
    // the remove this function
    // (All this is undocumented stuff...)
    public void onSelectionStart(WebView view) {
        // By default we cancel the selection again, thus disabling
        // text selection unless the chrome client supports it.
        // view.notifySelectDialogDismissed();
    }

});
person Khunt Nirav    schedule 16.07.2013
comment
Я попытался переопределить метод onSelectionStart, но такого метода для переопределения нет, я использовал copy-pase, и eclipse дает мне Метод onSelectionStart (WebView) из типа new WebChromeClient () {} никогда не используется локально - person sachin003; 31.07.2013

Вы также можете попробовать переопределить View.performLongClick (), который отвечает за вызов View.onLongPress (). Вы также можете попробовать перейти к событиям длительного нажатия родительского View. Или вплоть до Window.Callback для вашей активности (через Activity.getWindow (). Get / setCallback ()).

Мне интересно, есть ли другие способы отображения стандартного контекстного меню выбора, помимо события длительного нажатия, например, с помощью трекбола или аппаратной клавиатуры.

person Dane White    schedule 21.08.2013

в случае, если кто-то пытается просто удалить выделение текста по умолчанию, у меня была такая же проблема на Samsung Galaxy Tab на Android 4.1.2, и в итоге я написал свой собственный WebView:

public class CustomWebView extends WebView {

  public CustomWebView(Context context) {
    super(context);
    this.setUp(context);
  }

  public CustomWebView(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.setUp(context);
  }

  public CustomWebView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    this.setUp(context);
  }

  private void setUp(Context context) {
    this.setOnLongClickListener(new OnLongClickListener() {
      @Override
      public boolean onLongClick(View v) {
        return true;
      }
    });
    this.setLongClickable(false);
  }

  @Override
  public boolean performLongClick() {
    return true;
  }

}

и ссылаясь на него в моем xml:

<com...CustomWebView
    android:id="@+id/webview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />
person Guillaume    schedule 29.08.2013

Используйте setOnTouchListener () для реализации длительного нажатия

person shivaprakash    schedule 14.09.2013