Как сделать традиционный монгольский скрипт ListView в Android
Как сделать горизонтально прокручиваемый ListView для вертикального монгольского скрипта в приложениях Android?
Фон
Android имеет довольно хорошую поддержку для многих языков мира, даже для языков RTL, таких как арабский и иврит. Тем не менее, нет встроенной поддержки языков "сверху вниз", таких как традиционный монгольский (который все еще очень жив во Внутренней Монголии и не следует путать с кириллическим монгольским). На следующем рисунке показано направление текста с добавлением английского для ясности.
Поскольку эта функциональность не встроена в Android, она делает практически все аспекты разработки приложений чрезвычайно сложными. Это особенно верно для горизонтальных ListViews, которые не поддерживаются "из коробки" в Android. В Интернете также очень и очень мало информации. Существует ряд разработчиков приложений для традиционного монгольского языка, но, по коммерческим или иным причинам, они не делают свой код открытым исходным кодом.
Из-за этих трудностей я хотел бы составить серию вопросов Stackru, которые могут служить центральным местом для сбора ответов на некоторые из более сложных проблем программирования, связанных с разработкой традиционных монгольских приложений. Даже если вы не владеете монгольским языком, ваша помощь в просмотре кода, комментировании и вопросах, даче ответов или даже голосовании по данному вопросу будет принята с благодарностью.
Монгольская горизонтальная прокрутка ListView с вертикальным скриптом
Монгольский ListView должен иметь следующие требования:
- Прокрутка по горизонтали слева направо
- Сенсорные события работают так же, как с обычным ListView
- Пользовательские макеты поддерживаются так же, как в обычном ListView
Также необходимо поддерживать все, что поддерживает монгольский TextView:
- Поддерживает традиционный монгольский шрифт
- Отображает текст вертикально сверху вниз
- Перенос строки идет слева направо.
- Разрывы строк происходят в пробелах (как на английском)
На рисунке ниже показаны основные функции, которые должен иметь монгольский ListView:
Мой ответ ниже, но я приветствую другие способы решения этой проблемы.
Другие связанные вопросы в этой серии:
- Как сделать традиционный монгольский скрипт TextView в Android
- Как сделать традиционный монгольский скрипт EditText в Android
- Больше впереди... (Тост, Диалог, Меню)
IOS:
1 ответ
Обновить
RecyclerViews имеют горизонтальное расположение. Таким образом, относительно легко поместить текстовый объект Vertical Mongolian внутри одного из них. Вот пример из mongol-library
,
Смотрите этот ответ для общего решения использования RecyclerView
сделать горизонтально прокручиваемый список.
Старый ответ
Весьма прискорбно, что горизонтальные ListViews не предоставляются Android API. Есть несколько вопросов и ответов о Stackru, которые говорят о том, как их делать. Вот пара примеров:
Но когда я действительно пытался реализовать эти предложения, а также включить монгольский вертикальный текст, у меня было ужасное время. Где-то в моем поиске я нашел немного другой ответ. Это был класс, который вращал весь макет. Это было сделано путем расширения ViewGroup. Таким образом, все что угодно (включая ListView) может быть помещено в ViewGroup, и оно вращается. Все сенсорные события тоже работают.
Как я объяснил в своем ответе о монгольских TextViews, недостаточно просто повернуть монгольский текст. Этого было бы достаточно, если бы каждый элемент ListView (или другой текстовый элемент в ViewGroup) был только одной строкой, но вращение нескольких строк заставляет перенос строки идти в неправильном направлении. Тем не менее, зеркальное отображение макета по горизонтали, а также использование шрифта с вертикальным зеркальным отображением может преодолеть это, как показано на следующем рисунке.
Я адаптировал повернутый код ViewGroup для горизонтального зеркалирования.
public class MongolViewGroup extends ViewGroup {
private int angle = 90;
private final Matrix rotateMatrix = new Matrix();
private final Rect viewRectRotated = new Rect();
private final RectF tempRectF1 = new RectF();
private final RectF tempRectF2 = new RectF();
private final float[] viewTouchPoint = new float[2];
private final float[] childTouchPoint = new float[2];
private boolean angleChanged = true;
public MongolViewGroup(Context context) {
this(context, null);
}
public MongolViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
}
public View getView() {
return getChildAt(0);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final View view = getView();
if (view != null) {
measureChild(view, heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(resolveSize(view.getMeasuredHeight(), widthMeasureSpec),
resolveSize(view.getMeasuredWidth(), heightMeasureSpec));
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (angleChanged) {
final RectF layoutRect = tempRectF1;
final RectF layoutRectRotated = tempRectF2;
layoutRect.set(0, 0, right - left, bottom - top);
rotateMatrix.setRotate(angle, layoutRect.centerX(), layoutRect.centerY());
rotateMatrix.postScale(-1, 1);
rotateMatrix.mapRect(layoutRectRotated, layoutRect);
layoutRectRotated.round(viewRectRotated);
angleChanged = false;
}
final View view = getView();
if (view != null) {
view.layout(viewRectRotated.left, viewRectRotated.top, viewRectRotated.right,
viewRectRotated.bottom);
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
canvas.save();
canvas.rotate(-angle, getWidth() / 2f, getHeight() / 2f);
canvas.scale(-1, 1);
super.dispatchDraw(canvas);
canvas.restore();
}
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
invalidate();
return super.invalidateChildInParent(location, dirty);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
viewTouchPoint[0] = event.getX();
viewTouchPoint[1] = event.getY();
rotateMatrix.mapPoints(childTouchPoint, viewTouchPoint);
event.setLocation(childTouchPoint[0], childTouchPoint[1]);
boolean result = super.dispatchTouchEvent(event);
event.setLocation(viewTouchPoint[0], viewTouchPoint[1]);
return result;
}
}
Монгольский вертикально-зеркальный шрифт все еще должен быть установлен где-то еще. Я считаю, что проще всего сделать собственный TextView для этого:
public class MongolNonRotatedTextView extends TextView {
// This class does not rotate the textview. It only displays the Mongol font.
// For use with MongolLayout, which does all the rotation and mirroring.
// Constructors
public MongolNonRotatedTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public MongolNonRotatedTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MongolNonRotatedTextView(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/MongolMirroredFont.ttf");
setTypeface(tf);
}
}
Тогда пользовательский XML-макет элемента ListView может выглядеть примерно так:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rlListItem"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.MongolNonRotatedTextView
android:id="@+id/tvListViewText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"/>
</RelativeLayout>
Известные вопросы:
- Если вы внимательно посмотрите на изображение ниже, вы можете увидеть слабые горизонтальные и вертикальные линии вокруг текста. Хотя это изображение получено из приложения другого разработчика, в моем приложении появляются те же артефакты, когда я использую повернутую ViewGroup (но не когда я использую повернутый TextView). Если кто-то знает, откуда они, пожалуйста, оставьте мне комментарий!
- Это решение не имеет дело с отображением текста Unicode. Либо вам нужно использовать не-Unicode текст (не рекомендуется), либо вам нужно включить движок рендеринга в ваше приложение. (В настоящее время Android не поддерживает рендеринг смарт-шрифтов OpenType. Надеемся, что это изменится в будущем. IOS, для сравнения, поддерживает сложные шрифты рендеринга текста.) См. Эту ссылку для примера движка рендеринга монгольского языка Unicode.