具有工作选择、光标位置等的自定义旋转 EditText 视图 [英] Custom rotated EditText view with working selection, cursor location, etc

查看:19
本文介绍了具有工作选择、光标位置等的自定义旋转 EditText 视图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想做什么

我想制作一个旋转和翻转的 EditText 视图,它具有普通 EditText 视图的所有属性.

我的问题

我已经成功地(在 SO 用户的帮助下)制作了一个自定义的 EditText 视图,该视图既可以旋转又可以翻转.这是通过覆盖 onDraw 方法完成的.但是,光标和突出显示消失了,longtouch 事件仍然指示原始文本位置.基本上,视图被重绘,但触摸事件没有.

如何让触摸事件、突出显示和光标也被旋转和翻转?

我读到的

从头开始.它作为

旧答案

这仍然是一项正在进行的工作,所以我不会将其标记为已解决,但让我发布到目前为止的内容.它完成了我想做的大部分工作.基本上,我使用的是 TextView 而不是 EditText,因为 EditText 在旋转时会做很多奇怪的事情.

我有一个不闪烁的光标来响应触摸事件,但仍然不支持突出显示.代码如下:

public class MongolTextView extends TextView {私人TextPaint textPaint;私人油漆 cursorPaint = new Paint();私有布尔 mCursorIsVisible;私有 CursorTouchLocationListener 监听器;//命名基于预先旋转/镜像的值私人浮动 mCursorBaseY;私人浮动 mCursorBottomY;私人浮动 mCursorAscentY;//这是一个负数私人浮动 mCursorX;私有静态最终浮点数 CURSOR_THICKNESS = 2f;//构造函数public MongolTextView(上下文上下文,AttributeSet attrs,int defStyle){超级(上下文,属性,defStyle);在里面();}public MongolTextView(上下文上下文,AttributeSet attrs){超级(上下文,属性);在里面();}公共蒙古文本视图(上下文上下文){超级(上下文);在里面();}//该类要求镜像的蒙古文字体在 assets/fonts 文件夹中私人无效初始化(){//字体 tf = Typeface.createFromAsset(getContext().getAssets(),//字体/ChimeeWhiteMirrored.ttf");//setTypeface(tf);//使用上面注释的代码是在另一个应用程序中使用单一字体字体 tf = FontCache.get(SettingsActivity.FONT_DEFAULT, getContext());如果(tf!= null){设置字体(tf);}this.mCursorIsVisible = true;cursorPaint.setStrokeWidth(CURSOR_THICKNESS);cursorPaint.setColor(Color.BLACK);//TODO 应该和文本颜色一样}//如果不需要触摸功能,可以删除此界面公共接口 CursorTouchLocationListener {/*** 返回用于光标的触摸位置,以便您可以更新插入* 文本字符串中的位置.** @param 字形索引* 如果您使用的是* Unicode 字符串.*/公共无效 onCursorTouchLocationChanged(int glyphIndex);}@覆盖受保护的无效onMeasure(int widthMeasureSpec,int heightMeasureSpec){//交换高度和宽度super.onMeasure(heightMeasureSpec, widthMeasureSpec);setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());}@覆盖受保护的无效onDraw(帆布画布){textPaint = getPaint();textPaint.setColor(getCurrentTextColor());textPaint.drawableState = getDrawableState();画布.保存();//翻转和旋转画布canvas.translate(getWidth(), 0);画布.旋转(90);canvas.translate(0, getWidth());canvas.scale(1, -1);canvas.translate(getCompoundPaddingLeft(), getExtendedPaddingTop());//绘制光标如果(mCursorIsVisible){canvas.drawLine(mCursorX, mCursorBottomY, mCursorX, mCursorBaseY + mCursorAscentY,游标画);}getLayout().draw(画布);画布.restore();}公共无效showCursor(布尔可见){mCursorIsVisible = 可见;this.invalidate();//TODO 使光标闪烁}公共无效 setCursorColor(int 颜色) {cursorPaint.setColor(颜色);}公共无效 setCursorLocation(int characterOffset){布局布局 = this.getLayout();如果(布局!=空){尝试 {//这个方法会导致很多崩溃,所以只是围绕着//暂时尝试 catchint line = layout.getLineForOffset(characterOffset);mCursorX = layout.getPrimaryHorizo​​ntal(characterOffset);mCursorBaseY = layout.getLineBaseline(line);mCursorBottomY = layout.getLineBottom(line);mCursorAscentY = layout.getLineAscent(line);this.invalidate();} 捕捉(异常 e){e.printStackTrace();}}}公共类 InputWindowTouchListener 实现 OnTouchListener {@覆盖public boolean onTouch(View view, MotionEvent 事件) {布局布局 = ((TextView) 视图).getLayout();//为触摸事件交换 x 和 yint y = (int) event.getX();int x = (int) event.getY();如果(布局!= null){int line = layout.getLineForVertical(y);int offset = layout.getOffsetForHorizo​​ntal(line, x);mCursorX = layout.getPrimaryHorizo​​ntal(offset);mCursorBaseY = layout.getLineBaseline(line);mCursorBottomY = layout.getLineBottom(line);mCursorAscentY = layout.getLineAscent(line);//mCursorHeightY = layout.getLineTop(line);view.invalidate();开关 (event.getAction()) {案例 MotionEvent.ACTION_DOWN://handler.postDelayed(mLongPressed, 1000);listener.onCursorTouchLocationChanged(offset);休息;案例 MotionEvent.ACTION_UP://handler.removeCallbacks(mLongPressed);//通知宿主活动新的光标位置休息;}}返回假;}}公共无效 setCursorTouchLocationListener(CursorTouchLocationListener 侦听器){this.listener = 监听器;}}

如果您有更好的内容,请随时添加您自己的答案,或者如果您有任何要添加的内容以改进此问题(添加突出显示、让光标闪烁等),请随时发表评论.这段代码的最新版本应该在 github.

What I want to do

I want to make a rotated and flipped EditText view that has all of the properties of a normal EditText view.

My problem

I have successfully made (with much help from SO users) a custom EditText view that is both rotated and flipped. This was done by overriding the onDraw method. However, the cursor and highlighting are gone and a longtouch event still indicates the original text position. Basically, the view was redrawn but the touch events were not.

How do I get the touch events, highlighting, and cursor to also be rotated and flipped?

What I have read

EditText scale with selection (A similar problem but not quite the same.)

How to make a custom Edittext,so that it will look like as 45 degree rotated in android (@CommonsWare noted for one solution that addition work would need to be done with touch events. What is that work?)

http://developer.android.com/training/graphics/opengl/touch.html (Helpful, but I don't understand how to apply it in this situation.)

What I have tried

I made a custom view that extends EditText. In it overrode the onDraw method to rotate and flip the canvas. I overrode onMeasure to make the view have the right dimensions for the layout.

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.widget.EditText;

public class MongolEditText extends EditText {

// Constructors
public MongolEditText(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init();
}
public MongolEditText(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}
public MongolEditText(Context context) {
    super(context);
    init();
}

// This class requires the mirrored Mongolian font to be in the assets/fonts folder
private void init() {
    Typeface tf = Typeface.createFromAsset(getContext().getAssets(),
            "fonts/MongolChagaanMirrored.ttf");
    setTypeface(tf);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(heightMeasureSpec, widthMeasureSpec);
    setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}

@Override
protected void onDraw(Canvas canvas) {
    TextPaint textPaint = getPaint();
    textPaint.setColor(getCurrentTextColor());
    textPaint.drawableState = getDrawableState();

    canvas.save();

    canvas.translate(getWidth(), 0);
    canvas.rotate(90);
    canvas.translate(0, getWidth());
    canvas.scale(1, -1);

    canvas.translate(getCompoundPaddingLeft(), getExtendedPaddingTop());

    getLayout().draw(canvas);
    canvas.restore();
}
}

There is nothing special for the layout xml.

(Update) This question is another attempt at it but in the end I couldn't get it to work: Does invalidateDrawable() need to be overridden in addition to onDraw()?

Further explanation

In case you are wondering why in the world I want to rotate and flip an EditText view, here is the reason. Traditional Mongolian is written vertically in left to right columns. In combination with a vertically mirrored Mongolian font, rotating the text 90 degrees clockwise and flipping it produces readable output with correct line wrapping.

This is not an obscure or isolated problem. There are millions of users of traditional Mongolian but very few Android apps. Of these, I haven't found any that are open source. If I can get this to work, I want to make the code available to other developers.

Where I am looking now

(update)

I was thinking about creating a custom view (that extends View) from scratch to create something like a TextView. This TextView could be updated from apps to act like an EditText view. In this case I would only need to rotate the text 90 degrees with a normal font but not flip it. However, I would have to do my own line wrapping.

However, after reading @Chitrang's answer I think I can do something similar by just extending a TextView. Then I can avoid the trouble of doing my own line wrapping.

Picture Update

Mongolian is written from top to bottom and left to right. Right now I am using this key pad to move a cursor around the text, but I would like to be able to touch the screen to move the cursor to a position.

解决方案

Update

I ended up developing a vertical script MongolEditText from scratch. It is available as a part of mongol-library.

Here it is being used with two different third party keyboards.

Old answer

This is still a work in progress so I won't mark this as solved yet, but let me post what I have so far. It does most of what I wanted to do. Basically, I'm using TextView rather than EditText because EditText was doing to many strange things when rotated.

I have an unblinking cursor that responds to touch events but highlighting is still not supported. Here is the code:

public class MongolTextView extends TextView {

    private TextPaint textPaint;
    private Paint cursorPaint = new Paint();
    private boolean mCursorIsVisible;
    private CursorTouchLocationListener listener;

    // Naming is based on pre-rotated/mirrored values
    private float mCursorBaseY;
    private float mCursorBottomY;
    private float mCursorAscentY; // This is a negative number
    private float mCursorX;
    
    private static final float CURSOR_THICKNESS = 2f; 

    // Constructors
    public MongolTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public MongolTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MongolTextView(Context context) {
        super(context);
        init();
    }

    // This class requires the mirrored Mongolian font to be in the assets/fonts folder
    private void init() {
        //Typeface tf = Typeface.createFromAsset(getContext().getAssets(),
        //      "fonts/ChimeeWhiteMirrored.ttf");
        //setTypeface(tf);
        
        // Use the above commented code is using a single font in another application 
        Typeface tf = FontCache.get(SettingsActivity.FONT_DEFAULT, getContext());
        if(tf != null) {
            setTypeface(tf);
        }

        this.mCursorIsVisible = true;

        cursorPaint.setStrokeWidth(CURSOR_THICKNESS);
        cursorPaint.setColor(Color.BLACK); // TODO should be same as text color
        
    }

    // This interface may be deleted if touch functionality is not needed
    public interface CursorTouchLocationListener {

        /**
         * Returns the touch location to be used for the cursor so you can update the insert
         * location in a text string.
         * 
         * @param glyphIndex
         *            You will need to translate glyphIndex into a Unicode index if you are using a
         *            Unicode string.
         */
        public void onCursorTouchLocationChanged(int glyphIndex);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // swap the height and width
        super.onMeasure(heightMeasureSpec, widthMeasureSpec);
        setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
    }

    @Override
    protected void onDraw(Canvas canvas) {
        textPaint = getPaint();
        textPaint.setColor(getCurrentTextColor());
        textPaint.drawableState = getDrawableState();

        canvas.save();

        // flip and rotate the canvas
        canvas.translate(getWidth(), 0);
        canvas.rotate(90);
        canvas.translate(0, getWidth());
        canvas.scale(1, -1);
        canvas.translate(getCompoundPaddingLeft(), getExtendedPaddingTop());

        // draw the cursor
        if (mCursorIsVisible) {
            canvas.drawLine(mCursorX, mCursorBottomY, mCursorX, mCursorBaseY + mCursorAscentY,
                    cursorPaint);
        }

        getLayout().draw(canvas);

        canvas.restore();
    }

    public void showCursor(boolean visible) {
        mCursorIsVisible = visible;
        this.invalidate();
        // TODO make the cursor blink
    }
    
    public void setCursorColor(int color) {
        cursorPaint.setColor(color);
    }

    public void setCursorLocation(int characterOffset) {
        
        Layout layout = this.getLayout();
        
        if (layout!=null){
            
            try {
                // This method is giving a lot of crashes so just surrounding with 
                // try catch for now
                
                int line = layout.getLineForOffset(characterOffset);
                mCursorX = layout.getPrimaryHorizontal(characterOffset);
                mCursorBaseY = layout.getLineBaseline(line);
                mCursorBottomY = layout.getLineBottom(line);
                mCursorAscentY = layout.getLineAscent(line);
                
                this.invalidate();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    public class InputWindowTouchListener implements OnTouchListener {
        @Override
        public boolean onTouch(View view, MotionEvent event) {

            Layout layout = ((TextView) view).getLayout();

            // swapping x and y for touch events
            int y = (int) event.getX();
            int x = (int) event.getY();

            if (layout != null) {

                int line = layout.getLineForVertical(y);
                int offset = layout.getOffsetForHorizontal(line, x);

                mCursorX = layout.getPrimaryHorizontal(offset);
                mCursorBaseY = layout.getLineBaseline(line);
                mCursorBottomY = layout.getLineBottom(line);
                mCursorAscentY = layout.getLineAscent(line);
                //mCursorHeightY = layout.getLineTop(line);

                view.invalidate();
                
                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    //handler.postDelayed(mLongPressed, 1000);
                    listener.onCursorTouchLocationChanged(offset);
                    break;
                case MotionEvent.ACTION_UP:
                    //handler.removeCallbacks(mLongPressed);
                    // notify the host activity of the new cursor location
                    
                    break;
                }

            }

            return false;
        }
        
    }
    
    public void setCursorTouchLocationListener(CursorTouchLocationListener listener) {
        this.listener = listener;
    }
}

Feel free to add your own answer if you have something better or make a comment if you have something to add for improving this (adding highlighting, getting the cursor to blink, etc.). The most recent version of this code should be on github.

这篇关于具有工作选择、光标位置等的自定义旋转 EditText 视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆