如何在android应用程序中为垂直蒙古语脚本创建水平滚动列表视图?
背景
android对世界上许多语言都有很好的支持,甚至包括rtl语言,如阿拉伯语和希伯来语。但是,并没有内置的对像traditional Mongolian这样的自上而下语言的支持(它在内蒙古仍然非常活跃,不能与Cyrillic Mongolian混淆)。为清晰起见,下图显示了添加了英语的文本方向。
由于这个功能并没有内置到android中,它使得应用程序开发的几乎每一个方面都非常困难。这尤其适用于水平列表视图,android不支持这种即时可用的视图。网上的信息也非常,非常少。传统蒙古语有很多应用程序开发人员,但无论是出于商业原因还是其他原因,他们似乎都没有使自己的代码开源。
由于这些困难,我想提出一系列stackoverflow问题,这些问题可以作为收集与传统蒙古语应用程序开发相关的一些较难编程问题答案的中心位置。即使你不懂蒙古语,你的帮助审查代码,提出意见和问题,给出答案,甚至投票的问题将不胜感激。
蒙古语水平滚动垂直脚本ListView
蒙古语ListView需要具备以下要求:
从左向右水平滚动
触摸事件的工作方式与普通的列表视图相同
自定义布局的支持与普通列表视图中的相同
还需要支持aMongolian TextView支持的所有内容:
支持传统蒙古文字体
从上到下垂直显示文本
换行从左到右。
换行符出现在空格处(与英语相同)
下图显示了蒙古语ListView应具备的基本功能:
我的答案如下,但我欢迎其他方法来解决这个问题。
本系列其他相关问题:
How to make a traditional Mongolian script TextView in Android
How to make a traditional Mongolian script EditText in Android
更多…(吐司,对话,菜单)
网间网操作系统:
How do you make a vertical text UILabel and UITextView for iOS in Swift?
最佳答案
更新
RecyclerViews具有水平布局。因此,将垂直的蒙古语文本视图放在其中一个视图中相对容易。Here是mongol-library
中的一个示例。
有关使用RecyclerView
生成水平滚动列表的一般解决方案,请参见this answer。
旧答案
很不幸,android api没有提供水平列表视图。不过,有很多stackoverflow问答讨论了如何做到这一点。这里有几个样品:
How can I make a horizontal ListView in Android?
Horizontal ListView in Android?
但当我真的试图实施这些建议,并纳入蒙古语垂直文本,我有一个可怕的时间。在我的搜索中,我发现了一个稍微不同的答案。是a class that rotated an entire layout。它是通过扩展viewgroup实现的。这样,任何东西(包括listview)都可以放入viewgroup并进行旋转。所有的触摸事件也都起作用。
正如我在my answer about Mongolian 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;
}
}
不过,蒙古语垂直镜像字体仍需要在其他地方设置。我发现创建自定义文本视图最容易做到这一点:
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>
已知问题:
如果你仔细看下面的图片,你可以看到文本周围模糊的水平线和垂直线。尽管此图像来自另一个开发人员的应用程序,但当我使用旋转视图组时(但当我使用rotated TextView时)我的应用程序中会得到相同的工件。如果有人知道这些是从哪里来的,请给我留言!
此解决方案不处理呈现Unicode文本。您需要使用非Unicode文本(不鼓励使用),或者需要在应用程序中包含呈现引擎。(Android目前不支持OpenType SmartFont呈现。希望将来会有所改变。相比之下,ios确实支持复杂的文本呈现字体。)有关unicode蒙古语呈现引擎示例,请参见this link。