Search code examples
androidtextviewandroid-custom-view

How I can to do a custom TextView selectable in Android?


I have a custom TextView in Android and i trying to do this "selectable", but it's not working.

XML

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#e5d8bf"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <org.deiverbum.app.utils.ZoomTextView
            android:id="@+id/tv_Zoomable"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/app_name"
            android:fontFamily="@font/roboto_regular"
            android:textColor="#272626"
            android:textIsSelectable="true"
            android:textSize="@dimen/default_font" />
    </ScrollView>
</RelativeLayout>

I try to do that programatically and nothing...

    mTextView.setTextIsSelectable(true);

And I try in te constructor of the class:

public class ZoomTextView extends android.support.v7.widget.AppCompatTextView implements View.OnTouchListener {
    //....

    public ZoomTextView(Context context) {
        super(context);
        this.setTextIsSelectable(true);
    }

    public ZoomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setTextIsSelectable(true);
    }

    public ZoomTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.setTextIsSelectable(true);
    }

    // ....

How i can to do this custom textview selectable?

Edit: New custum class

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;    

public class ZoomTextView extends android.support.v7.widget.AppCompatTextView implements View.OnTouchListener {
    final static float STEP = 200;
    private static final String TAG = "ZoomTextView";
    float mRatio = 13.0f;
    int mBaseDist;
    float mBaseRatio;
    private float zoomLimit = 7.0f;


    public ZoomTextView(Context context) {
        super(context);
        this.setTextIsSelectable(true);
        //    initialize();
    }

    public ZoomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setTextIsSelectable(true);

        //     initialize();
    }

    public ZoomTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.setTextIsSelectable(true);
        //       initialize();
    }

    /*
    private void initialize() {
        defaultSize = getTextSize();
        mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());

    }
*/

    /***
     * @param zoomLimit
     * Default value is 3, 3 means text can zoom 3 times the default size
     */

    public void setZoomLimit(float zoomLimit) {
        this.zoomLimit = zoomLimit;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
/*

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent ev) {
        super.onTouchEvent(ev);
        mScaleDetector.onTouchEvent(ev);
        return true;
    }
*/
    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        if (event.getPointerCount() == 2) {
            int action = event.getAction();
            int pureaction = action & MotionEvent.ACTION_MASK;
            if (pureaction == MotionEvent.ACTION_POINTER_DOWN) {
                mBaseDist = getDistance(event);
                mBaseRatio = mRatio;
            } else {
                float delta = (getDistance(event) - mBaseDist) / STEP;
                float multi = (float) Math.pow(2, delta);
                mRatio = Math.min(1024.0f, Math.max(0.1f, mBaseRatio * multi));
                this.setTextSize(mRatio + 13);
            }
        }
        return true;

    }

    int getDistance(MotionEvent event) {
        int dx = (int) (event.getX(0) - event.getX(1));
        int dy = (int) (event.getY(0) - event.getY(1));
        return (int) (Math.sqrt(dx * dx + dy * dy));
    }

    public boolean onTouch(View v, MotionEvent event) {
        return false;
    }
    protected void onDraw (Canvas canvas) {
        super.onDraw(canvas);
    }


    @Override
    public void setTextIsSelectable(boolean selectable) {
        super.setTextIsSelectable(selectable);
    }

    @Override
    public boolean isTextSelectable() {
        return super.isTextSelectable();
    }
    
}

Solution

  • Your onTouchEvent() is interfering with the selectability of the text in the custom view. You can make the custom view text selectable by adding the following to the start of onTouchEvent():

    super.onTouchEvent(event);
    

    This may be enough to solve your problem, but it may break what else you are trying to do in onTouchEvent(). Try the following to incorporate text selection and zooming:

    public boolean onTouchEvent(@NonNull MotionEvent event) {
        if (event.getPointerCount() == 2) {
            int action = event.getAction();
            int pureaction = action & MotionEvent.ACTION_MASK;
            if (pureaction == MotionEvent.ACTION_POINTER_DOWN) {
                mBaseDist = getDistance(event);
                mBaseRatio = mRatio;
            } else {
                float delta = (getDistance(event) - mBaseDist) / STEP;
                float multi = (float) Math.pow(2, delta);
                mRatio = Math.min(1024.0f, Math.max(0.1f, mBaseRatio * multi));
                this.setTextSize(mRatio + 13);
            }
        } else {
            return super.onTouchEvent(event);
        }
        return true;
    }
    

    If you are unclear how the Android touch events work take a look at these:

    Input Events Overview
    How are Android touch events delivered?