如何在Android中制作传统的蒙古文字ListView(How to make a traditional Mongolian script ListView in Android)
如何在Android应用程序中为垂直蒙古语脚本水平滚动ListView?
背景
Android对世界上许多语言都有相当好的支持,甚至包括阿拉伯语和希伯来语等RTL语言。 然而,对于像传统蒙古语这样的自上而下的语言没有内置支持(在内蒙古仍然非常活跃,不要与西里尔语蒙古语混淆)。 为清晰起见,下图显示了添加了英语的文本方向。
由于此功能未内置于Android中,因此几乎使应用程序开发的每个方面都非常困难。 对于水平ListViews尤其如此,Android中不支持开箱即用。 在线提供的信息非常非常少。 有许多针对传统蒙古语的应用程序开发人员,但无论是出于商业原因还是其他原因,他们似乎都没有使他们的代码成为开源代码。
由于存在这些困难,我想提出一系列StackOverflow问题,这些问题可以作为收集与传统蒙古应用程序开发相关的一些更困难的编程问题的答案的中心位置。 即使您没有蒙古语识字,您也可以阅读帮助,查看代码,提出意见和问题,给出答案,甚至对问题进行投票。
Mongolian水平滚动ListView与垂直脚本
Mongolian ListView需要具备以下要求:
- 从左到右水平滚动
- 触摸事件与普通ListView的工作方式相同
- 自定义布局的支持与普通ListView相同
还需要支持Mongolian TextView支持的所有内容:
- 支持传统的蒙古语字体
- 从上到下垂直显示文本
- 换行从左到右。
- 换行发生在一个空间(与英语相同)
下图显示了Mongolian ListView应具备的基本功能:
我的答案如下,但我欢迎其他方法来解决这个问题。
本系列中的其他相关问题:
- 如何在Android中制作传统的蒙古文字TextView
- 如何在Android中制作传统的蒙古文字EditText
- 更多......(Toast,Dialog,Menu)
iOS版:
How do you make a horizontally scrolling ListView for vertical Mongolian script in Android apps?
Background
Android has fairly good support for many of the world's languages, even RTL languages like Arabic and Hebrew. However, there is no built in support for top-to-bottom languages like traditional Mongolian (which is still very much alive in Inner Mongolia and not to be confused with Cyrillic Mongolian). The following graphic shows the text direction with English added for clarity.
Since this functionality is not built into Android, it makes almost every single aspect of app development extremely difficult. This is expecially true with horizontal ListViews, which are not supported out of the box in Android. There is also very, very little information available online. There are a number of app developers for traditional Mongolian, but whether it is for commercial reasons or otherwise, they do not seem to make their code open source.
Because of these difficulties I would like to make a series of StackOverflow questions that can serve as a central place to collect answers for some of the more difficult programming problems related to traditional Mongolian app development. Even if you are not literate in Mongolian, your help reviewing the code, making comments and questions, giving answers or even up-voting the question would be appreciated.
Mongolian Horizontally Scrolling ListView with Vertical Script
A Mongolian ListView needs to have the following requirements:
- Scrolls horizontally from left to right
- Touch events work the same as with a normal ListView
- Custom layouts are supported the same as in a normal ListView
Also needs to support everything that a Mongolian TextView would support:
- Supports a traditional Mongolian font
- Displays text vertically from top to bottom
- Line wrapping goes from left to right.
- Line breaks occur at a space (same as English)
The image below shows the basic functionality a Mongolian ListView should have:
My answer is below, but I welcome other ways of solving this problem.
Other related questions in this series:
- How to make a traditional Mongolian script TextView in Android
- How to make a traditional Mongolian script EditText in Android
- More to come... (Toast, Dialog, Menu)
iOS:
满意答案
更新
RecyclerViews具有水平布局。 因此,将Vertical Mongolian TextView放在其中一个中相对容易。 这是
mongol-library
一个例子。有关使用
RecyclerView
制作水平滚动列表的一般解决方案,请参阅此答案 。老答案
非常不幸的是Android API没有提供水平ListView。 不过,有很多StackOverflow问答都谈到了如何做。 以下是几个样本:
但是,当我真正尝试实施这些建议以及合并蒙古文垂直文本时,我度过了一段可怕的时光。 在我搜索的某处,我发现了一个略有不同的答案。 这是一个旋转整个布局的类 。 它是通过扩展ViewGroup来实现的。 通过这种方式,任何东西(包括ListView)都可以放在ViewGroup中并旋转。 所有的触摸事件也都有效。
正如我在关于蒙古文字视图的回答中所解释的那样,简单地旋转蒙古文本是不够的。 如果每个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; } }
不过,Mongolian垂直镜像字体仍然需要设置在其他地方。 我发现最简单的做一个自定义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时,我在我的应用程序中获得了相同的工件(但是当我使用旋转的TextView时却没有)。 如果有人知道这些来自哪里,请给我留言!
- 此解决方案不涉及呈现Unicode文本。 您需要使用非Unicode文本(不鼓励),或者需要在应用程序中包含渲染引擎。 (Android目前不支持OpenType smartfont渲染。希望将来会改变。相比之下,iOS支持复杂的文本渲染字体。)请参阅此链接以获取Unicode蒙古语渲染引擎示例。
Update
RecyclerViews have a horizontal layout. So it is relatively easy to put a Vertical Mongolian TextView inside one of these. Here is an example from
mongol-library
.See this answer for a general solution to using a
RecyclerView
to make a horizontally scrolling list.Old answer
It is quite unfortunate that horizontal ListViews are not provided by the Android API. There are a number of StackOverflow Q&As that talk about how to do them, though. Here are a couple samples:
But when I actually tried to implement these suggestions as well as incorporate Mongolian vertical text, I was having a terrible time. Somewhere in my search I found a slightly different answer. It was a class that rotated an entire layout. It did so by extending ViewGroup. In this way anything (including a ListView) can be put in the ViewGroup and it gets rotated. All the touch events work, too.
As I explained in my answer about Mongolian TextViews, it is not enough to simply rotate Mongolian text. That would be enough if every ListView item (or other text element in the ViewGroup) was only a single line, but rotating multiple lines make the line wrap go the wrong direction. However, mirroring the layout horizontally and also using a vertically mirrored font can overcome this, as is shown in the following image.
I adapted the rotated ViewGroup code to also do the horizontal mirroring.
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; } }
The Mongolian vertically mirrored font still needs to be set somewhere else, though. I find it easiest to make a custom TextView to do it:
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); } }
Then the custom ListView item xml layout can look something like this:
<?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>
Known issues:
- If you look carefully at the image below you can see faint horizontal and vertical lines around the text. Although this image comes from another developer's app, I am getting the same artifacts in my app when I use the rotated ViewGroup (but not when I use the rotated TextView). If anyone knows where these are coming from, please leave me a comment!
- This solution does not deal with rendering the Unicode text. Either you need to use non-Unicode text (discouraged) or you need to include a rendering engine in your app. (Android does not support OpenType smartfont rendering at this time. Hopefully this will change in the future. iOS, by comparison does support complex text rendering fonts.) See this link for a Unicode Mongolian rendering engine example.
相关问答
更多Android ListView不会滚动。(Android ListView does not scroll. Tried everything)
Android listview不可滚动(Android listview not scrollable)
Listview到android中的pdf(Listview to pdf in android)
如何在android中制作ListView?(How to make a ListView in android? [closed])
如何在Android中制作传统的蒙古文字ListView(How to make a traditional Mongolian script ListView in Android)
Android:ListView具有奇怪的边距(Android: ListView has strange margins)
ListView prob - Android(ListView prob - Android)
与listview android的相对布局(Relative layout with listview android)
如何让Android ListView刷新(how to make Android ListView refresh)
相关文章
更多Android开发学习之快速实现圆角ListView
ListView具有多种item布局——实现微信对话列
android的listview里怎么动态加载啊 就是先下载20个 滑到20了 在加载20个
android的listview里怎么动态加载啊 就是先下载20个 滑到20了 在加载20个
如何在 ListView 中显示 RadioButton???
[How to] Make custom search with Nutch(v 1.0)?(转)
每日英语:Man Without a Country
Becoming a data scientist
疯狂Android讲义
Script.NET Perl解释器代码已经在GitHub开源发布
最新问答
更多Python / IPython奇怪的不可重现列表索引超出范围错误(Python/IPython strange non reproducible list index out of range error)
故事板从表格单元中延伸出来并显示其详细信息披露按钮(storyboard segue from a tablecell an its Detail Disclosure Button)
我们可以将Gherkin功能文件与testcomplete集成(Can we integrate Gherkin feature files with testcomplete)
TrustAllCertificatesCallback被忽略(TrustAllCertificatesCallback is ignored)
返回嵌套元素的索引(Return index of nested element)
在数组中重新编号元素的有效方法(Efficient way of re-numbering elements in an array)
Express app定义的`static`文件夹无法正常工作(Express app defined `static` folder not working properly)
Javascript错误:未捕获TypeError:无法读取null的属性'value'[duplicate](Javascript error: Uncaught TypeError: Cannot read property 'value' of null [duplicate])
使用node.js和socket.io每秒广播一次(Using node.js and socket.io to broadcast every second)
如何让XMLSerializer将命名空间添加到嵌套对象中的属性?(How do I get the XMLSerializer to add namespaces to attributes in nested objects?)
Copyright ©2023 656463.com All Rights Reserved.滇ICP备2022006988号-50
本站部分内容来源于互联网,仅供学习和参考使用,请莫用于商业用途。如有侵犯你的版权,请联系我们,本站将尽快处理。谢谢合作!