Пользовательское вращаемое представление EditText с рабочим выделением, расположением курсора и т. Д.
Что я хочу сделать
Я хочу сделать перевернутое и перевернутое представление EditText, которое имеет все свойства обычного представления EditText.
Моя проблема
Я успешно сделал (с большой помощью пользователей SO) настраиваемое представление EditText, которое поворачивается и переворачивается. Это было сделано путем переопределения метода onDraw. Однако курсор и подсветка исчезли, и событие longtouch все еще указывает исходную текстовую позицию. По сути, представление было перерисовано, а события касания - нет.
Как сделать так, чтобы сенсорные события, выделение и курсор также вращались и переворачивались?
Что я прочитал
Масштаб EditText с выбором (похожая проблема, но не совсем то же самое.)
Как создать собственный Edittext, чтобы он выглядел как повернутый на 45 градусов в Android (@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();
}
}
Там нет ничего особенного для макета XML.
(Обновление) Этот вопрос является еще одной попыткой, но в итоге я не смог заставить его работать: нужно ли переопределить invalidateDrawable() в дополнение к onDraw()?
Дальнейшее объяснение
Если вам интересно, почему в мире я хочу повернуть и перевернуть представление EditText, вот причина. Традиционный монгольский написан вертикально в колонках слева направо. В сочетании с монгольским шрифтом с вертикальным зеркальным отображением поворот текста на 90 градусов по часовой стрелке и его переворачивание обеспечивает читабельный вывод с правильной переносом строк.
Это не скрытая или изолированная проблема. Есть миллионы пользователей традиционных монгольских, но очень мало приложений для Android. Из них я не нашел ни одного с открытым исходным кодом. Если я смогу заставить это работать, я хочу сделать код доступным для других разработчиков.
Где я сейчас смотрю
(Обновить)Я думал о создании собственного представления (которое расширяет View
) с нуля создать что то вроде TextView
, это TextView
может быть обновлен из приложений, чтобы действовать как EditText
Посмотреть. В этом случае мне нужно только повернуть текст на 90 градусов обычным шрифтом, но не переворачивать его. Тем не менее, я должен был бы сделать свою собственную перенос строки.
Однако после прочтения ответа @ Читранга я думаю, что могу сделать нечто подобное, просто расширив TextView. Тогда я могу избежать проблем с выполнением моей собственной переноса строк.
Обновление изображения
Монгольский написан сверху вниз и слева направо. Прямо сейчас я использую эту клавиатуру для перемещения курсора по тексту, но я хотел бы иметь возможность касаться экрана, чтобы переместить курсор в позицию.
5 ответов
Обновить
Я закончил разработку вертикального сценария 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;
}
}
Не стесняйтесь добавлять свой собственный ответ, если у вас есть что-то лучше, или комментируйте, если у вас есть что добавить для улучшения этого (добавление выделения, заставление курсора мигать и т. Д.) Самая последняя версия этого кода должна быть на github.
Попытка получить частичное решение с небольшими изменениями, проверьте, если вы хотите.
1) Установите android:gravity="top|left" в вашем объявлении edittext внутри xml.
2) Заметил, что без вызова супер- метода внутри метода onDraw, невозможно отобразить курсор. Я позвонил,
super.onDraw(canvas);
вместо,
getLayout().draw(canvas);
3) Для сенсорных событий я пытался поменять координаты x и y. Так что вы можете иметь курсор в соответствии с сенсорным событием.(Только что попробовал и все заработало, повезло:))
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
event.setLocation(event.getY(), event.getX());
return super.onTouchEvent(event);
}
Прокомментируйте эту строку внутри метода onDraw для точного события касания (найдено методом проб и ошибок).
canvas.translate(getCompoundPaddingLeft(), getExtendedPaddingTop());
4) Я не мог ничего сделать с подсветкой или выделением текста.
?) Другое решение: если вы думаете, что, как вы получаете поддержку языка RTL, работаете с edittext, вам просто нужно повернуть его. Но, к сожалению, не работает должным образом с Android. Справка: Как работать с языками RTL в версиях Android до 4.2? и настройка Android с TextView для текста на иврите?
Атрибут есть для EditText для вращения. Он прост и удобен в использовании. Думаю, это может помочь.
<EditText
android:id="@+id/editTextNumber"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:rotation="90"
android:layout_marginBottom="10dp"
android:hint="Enter Mobile Number"
android:textAppearance="?android:attr/textAppearanceLarge" />
Все намного проще. Все, что вам нужно, уже встроено в класс View: http://developer.android.com/reference/android/view/View.html Немного более сложное решение, если предыдущее не будет работать для вас некоторыми причина: http://developer.android.com/reference/android/view/animation/RotateAnimation.html Он также используется для поворота видов (но в большинстве случаев анимирован, однако вы можете использовать его с нулевой продолжительностью перехода).
Пожалуйста, дайте мне знать, если у вас возникнут дополнительные вопросы (угадайте, нет, есть метод и класс, объясняющий сами себя).
Вращая только холст - вы вращаете только изображение на экране. setRotation также обрабатывает все события, макет потока и т. д., поэтому он должен работать нормально в вашем случае!
Надеюсь, это поможет!
Попробуй это:
<EditText
android:id="@+id/ed1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textView1"
android:layout_centerHorizontal="true"
android:layout_marginTop="47dp"
android:digits="1234567890"
android:ems="10"
android:singleLine="true"
android:cursorVisible="true"
android:hint="Refreshing time in ms" />