自定义旋转的EditText视图,包含工作选择,光标位置等

Sur*_*gch 18 android rotation android-edittext mongolian-vertical-script

我想做的事

我想创建一个旋转和翻转的EditText视图,该视图具有普通EditText视图的所有属性.

我的问题

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

如何旋转和翻转触摸事件,突出显示和光标?

我读过的

带选择的EditText比例(类似的问题,但不完全相同.)

如何制作一个自定义的Edittext,以便它看起来像在Android中旋转45度(@CommonsWare注意到一个解决方案,需要通过触摸事件完成添加工作.那是什么工作?)

http://developer.android.com/training/graphics/opengl/touch.html(有帮助,但我不明白如何在这种情况下应用它.)

我试过了什么

我创建了一个扩展EditText的自定义视图.在其中覆盖onDraw方法以旋转和翻转画布.我重写onMeasure以使视图具有适合布局的尺寸.

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();
}
}
Run Code Online (Sandbox Code Playgroud)

布局xml没有什么特别之处.

(更新)这个问题是另一种尝试,但最终我无法使它工作:除了onDraw()之外还需要覆盖invalidateDrawable()吗?

进一步说明

如果你想知道为什么在世界上我想旋转和翻转EditText视图,这就是原因.传统的蒙古语是从左到右垂直写的.结合垂直镜像蒙古字体,顺时针旋转文本90度并翻转它产生可读输出和正确的换行.

这不是一个模糊或孤立的问题.有数百万传统蒙古语用户,但很少有Android应用程序.其中,我还没有发现任何开源软件.如果我可以使用它,我想让其他开发人员可以使用该代码.

我现在在哪里看

(更新)

我正在考虑View从头开始创建一个自定义视图(扩展)来创建像TextView.这TextView可以从应用程序更新,就像一个EditText视图.在这种情况下,我只需要用正常字体旋转文本90度但不要翻转它.但是,我必须做自己的换行.

但是,在阅读@Chitrang的回答后,我想我可以通过扩展TextView来做类似的事情.然后我可以避免做自己换行的麻烦.

图片更新

在此输入图像描述

蒙古语是从上到下,从左到右书写的.现在我正在使用此键盘在文本周围移动光标,但我希望能够触摸屏幕将光标移动到某个位置.

Sur*_*gch 1

更新

我最终从头开始开发了一个垂直脚本MongolEditText。它可以作为mongol-library

这里它与两个不同的第三方键盘一起使用。

在此输入图像描述

旧答案

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

我有一个不闪烁的光标,可以响应触摸事件,但仍然不支持突出显示。这是代码:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您有更好的东西,请随意添加您自己的答案,或者如果您需要添加一些内容来改进此问题(添加突出显示、让光标闪烁等),请发表评论。该代码的最新版本应该位于github上。