如何在Android中制作传统的蒙古文字ListView

时间:2015-04-20 15:20:00

标签: android listview horizontal-scrolling vertical-text mongolian-vertical-script

如何在Android应用中为垂直蒙古语脚本水平滚动ListView?

背景

Android对许多世界语言都有相当好的支持,甚至包括阿拉伯语和希伯来语等RTL语言。但是,没有像traditional Mongolian这样的自上而下语言的内置支持(在内蒙古仍然非常活跃,不要与Cyrillic Mongolian混淆)。为清晰起见,下图显示了添加了英语的文本方向。

enter image description here

由于此功能未内置于Android中,因此几乎使应用程序开发的每个方面都非常困难。对于水平ListViews尤其如此,Android中不支持开箱即用。在线提供的信息非常非常少。有许多针对传统蒙古语的应用程序开发人员,但无论是出于商业原因还是其他原因,他们似乎都没有使他们的代码成为开源代码。

由于存在这些困难,我想提出一系列StackOverflow问题,这些问题可以作为收集与传统蒙古应用程序开发相关的一些更困难的编程问题的答案的中心位置。即使您没有蒙古语识字,我们也会感谢您帮助查看代码,提出意见和问题,给出答案甚至是对问题进行投票。

使用垂直脚本的Mongolian Horizo​​ntal Scrolling ListView

Mongolian ListView需要具备以下要求:

  • 从左向右水平滚动
  • 触摸事件与普通ListView
  • 的工作方式相同
  • 自定义布局的支持与普通ListView
  • 相同

还需要支持Mongolian TextView支持的所有内容:

  • 支持传统的蒙古语字体
  • 从上到下垂直显示文字
  • 换行从左到右。
  • 换行符出现在空格处(与英语相同)

下图显示了Mongolian ListView应具备的基本功能:

enter image description here

我的回答如下,但我欢迎其他方法来解决这个问题。

本系列中的其他相关问题:

的iOS:

1 个答案:

答案 0 :(得分:2)

更新

RecyclerViews具有水平布局。因此,将Vertical Mongolian TextView放在其中一个中相对容易。 Heremongol-library的一个示例。

enter image description here

有关使用RecyclerView制作水平滚动列表的一般解决方案,请参阅this answer

旧答案

非常不幸的是Android API没有提供水平ListView。然而,有很多StackOverflow Q& As谈论如何做到这一点。以下是几个样本:

但是,当我真正尝试实施这些建议以及合并蒙古文垂直文本时,我度过了一段可怕的时光。在我搜索的某处,我发现了一个略有不同的答案。那是a class that rotated an entire layout。它通过扩展ViewGroup来实现。通过这种方式,任何东西(包括ListView)都可以放在ViewGroup中并旋转。所有的触摸事件也都有效。

正如我在my answer about Mongolian TextViews中解释的那样,仅仅旋转蒙古文本是不够的。如果每个ListView项(或ViewGroup中的其他文本元素)只是一行,那就足够了,但是旋转多行会使换行方向错误。但是,水平镜像布局并使用垂直镜像字体可以克服此问题,如下图所示。

enter image description here

我调整了旋转的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);

    }
}

然后自定义ListView项目xml布局可能如下所示:

<?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时,我在我的应用程序中获得相同的工件(但不是在我使用rotated TextView时)。如果有人知道这些来自哪里,请给我留言!

enter image description here

  • 此解决方案不涉及呈现Unicode文本。您需要使用非Unicode文本(不鼓励),或者需要在应用程序中包含渲染引擎。 (Android目前不支持OpenType smartfont渲染。希望将来会有所改变。相比之下,iOS支持复杂的文本渲染字体。)请参阅this link获取Unicode蒙古语渲染引擎示例。