Android 如何布局文字使其在图像周围流动
你能告诉我是否有一种方法来编排文本吗 围绕图像? 像这样:Android 如何布局文字使其在图像周围流动,android,android-layout,Android,Android Layout,你能告诉我是否有一种方法来编排文本吗 围绕图像? 像这样: ------ text text text | | text text text ----- text text text text text text text text text text text <uk.co.deanwild.flowtextview.FlowTextView android:id="@+id/ftv" android:layout_width="fill_parent"
------ text text text
| | text text text
----- text text text
text text text text
text text text text
<uk.co.deanwild.flowtextview.FlowTextView
android:id="@+id/ftv"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:padding="10dip"
android:src="@drawable/android"/>
</uk.co.deanwild.flowtextview.FlowTextView>
关于这个问题,我得到了一位安卓开发者的回复。但我不确定他做我自己版本的TextView是什么意思?谢谢你的提示
2010年2月8日星期一晚上11:05,罗曼·盖伊写道:
嗨
仅使用提供的小部件和布局是不可能的。你
可以编写自己版本的TextView来实现这一点,但不应该这样
很难
“但我不确定他做我自己版本的TextView是什么意思?”
他的意思是,您可以扩展android.widget.TextView类(或Canvas或其他一些可渲染曲面),并实现您自己的覆盖版本,该版本允许嵌入图像和文本在其周围流动
这可能需要大量的工作,具体取决于您的通用性。现在这是可能的,但仅适用于版本高于或等于2.2的手机,使用API 8中提供的
android.text.style.LeadingMarginSpan.LeadingMarginSpan.2
接口
下面是示例,虽然不是英文的,但是您可以直接从下载示例的源代码
如果要使应用程序与旧设备兼容,可以在不使用浮动文本的情况下显示不同的布局。
以下是一个例子:
布局(旧版本的默认布局,新版本的默认布局将以编程方式更改)
MyReadingMarginSpan2类(更新以支持API 21)
这是应用程序在Android 2.2设备上的外观:
这是针对Android 2.1设备的:
以下是对FlowTextHelper的改进(来自Vortex的回复)。 我添加了在文本和图像之间添加额外填充的可能性,并改进了线条计算,以将填充也考虑在内。 享受吧
这个问题似乎和我的问题一样
我使用flowtext库找到了解决方案请找到第一个答案到目前为止它可能对您有所帮助Vorrtex和Ronen的答案对我有效,除了一个细节-在图像周围包装文本后,图像下方和对面有一个奇怪的“负”边距。我发现当在SpannableString上设置跨距时,我改变了
ss.setSpan(new MyLeadingMarginSpan2(lines, width), 0, ss.length(), 0);
到
它在图像之后停止了span。可能不是所有情况下都需要,但我想我会分享 我可以为您提供更舒适的 MyReadingMarginSpan2类
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/thumbnail_view"
android:src="@drawable/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView android:id="@+id/message_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/thumbnail_view"
android:textSize="18sp"
android:text="@string/text" />
</RelativeLayout>
class FlowTextHelper {
private static boolean mNewClassAvailable;
static {
if (Integer.parseInt(Build.VERSION.SDK) >= 8) { // Froyo 2.2, API level 8
mNewClassAvailable = true;
}
}
public static void tryFlowText(String text, View thumbnailView, TextView messageView, Display display){
// There is nothing I can do for older versions, so just return
if(!mNewClassAvailable) return;
// Get height and width of the image and height of the text line
thumbnailView.measure(display.getWidth(), display.getHeight());
int height = thumbnailView.getMeasuredHeight();
int width = thumbnailView.getMeasuredWidth();
float textLineHeight = messageView.getPaint().getTextSize();
// Set the span according to the number of lines and width of the image
int lines = (int)FloatMath.ceil(height / textLineHeight);
//For an html text you can use this line: SpannableStringBuilder ss = (SpannableStringBuilder)Html.fromHtml(text);
SpannableString ss = new SpannableString(text);
ss.setSpan(new MyLeadingMarginSpan2(lines, width), 0, ss.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
messageView.setText(ss);
// Align the text with the image by removing the rule that the text is to the right of the image
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)messageView.getLayoutParams();
int[]rules = params.getRules();
rules[RelativeLayout.RIGHT_OF] = 0;
}
}
MyLeadingMarginSpan2(Context cc,int textSize,int height,int width) {
int pixelsInLine=(int) (textSize*cc.getResources().getDisplayMetrics().scaledDensity);
if (pixelsInLine>0 && height>0) {
this.lines=height/pixelsInLine;
} else {
this.lines=0;
}
this.margin=width;
}
现在你可以使用图书馆:。像这样:
------ text text text
| | text text text
----- text text text
text text text text
text text text text
<uk.co.deanwild.flowtextview.FlowTextView
android:id="@+id/ftv"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:padding="10dip"
android:src="@drawable/android"/>
</uk.co.deanwild.flowtextview.FlowTextView>
Vortex的答案对我来说并不奏效,但我从中吸取了很多教训,并提出了自己的解决方案。这是:
package ie.moses.keepitlocal.util;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.IntRange;
import android.text.Layout;
import android.text.style.LeadingMarginSpan;
import android.view.View;
import android.widget.TextView;
import ie.moses.keepitlocal.util.MeasurementUtils;
import ie.moses.keepitlocal.util.TextUtils;
import static com.google.common.base.Preconditions.checkArgument;
public class WrapViewSpan implements LeadingMarginSpan.LeadingMarginSpan2 {
private final Context _context;
private final int _lineCount;
private int _leadingMargin;
private int _padding;
public WrapViewSpan(View wrapeeView, TextView wrappingView) {
this(wrapeeView, wrappingView, 0);
}
/**
* @param padding Padding in DIP.
*/
public WrapViewSpan(View wrapeeView, TextView wrappingView, @IntRange(from = 0) int padding) {
_context = wrapeeView.getContext();
setPadding(padding);
int wrapeeHeight = wrapeeView.getHeight();
float lineHeight = TextUtils.getLineHeight(wrappingView);
int lineCnt = 0;
float linesHeight = 0F;
while ((linesHeight += lineHeight) <= wrapeeHeight) {
lineCnt++;
}
_lineCount = lineCnt;
_leadingMargin = wrapeeView.getWidth();
}
public void setPadding(@IntRange(from = 0) int paddingDp) {
checkArgument(paddingDp >= 0, "padding cannot be negative");
_padding = (int) MeasurementUtils.dpiToPixels(_context, paddingDp);
}
@Override
public int getLeadingMarginLineCount() {
return _lineCount;
}
@Override
public int getLeadingMargin(boolean first) {
if (first) {
return _leadingMargin + _padding;
} else {
return _padding;
}
}
@Override
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline,
int bottom, CharSequence text, int start, int end,
boolean first, Layout layout) {
}
}
我需要全局布局侦听器,以便为getWidth()
和getHeight()
获取正确的值
结果如下:
使用kotlin和androidx尝试这个简单的实现。 首先,创建主要的span帮助器类:
class LeadingSpan(private val line: Int, private val margin: Int) : LeadingMarginSpan.LeadingMarginSpan2 {
override fun drawLeadingMargin(canvas: Canvas?, paint: Paint?, x: Int, dir: Int, top: Int, baseline: Int, bottom: Int, text: CharSequence?, start: Int, end: Int, first: Boolean, layout: Layout?) {}
override fun getLeadingMargin(first: Boolean): Int = if (first) margin else 0
override fun getLeadingMarginLineCount(): Int = line
}
然后使用RelativeLayout
创建布局:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/about_desc"
android:text="@string/about_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
根据您的可绘制高度更改
帮助。LeadingSpan(5,logoImage.intrinsicWidth+10)中的数字5
。silverburgh:您找到了可以共享的解决方案吗?这是一个很可能的解决方案。这在网络上很容易做到。我现在跳过这个功能。你可以使用。看一看,你可以使用一个简单的条件来代替Class.forName技巧:如果(Build.VERSION.SDK_INTHtml.fromHtml
不适用于任何Html,它只支持带有少量标记的简单Html。非常好的工作,我几乎浪费了一天的时间…非常感谢!!不过这也为图像后面的行添加了一个右边距(因此,文本永远不会完全交叉)。你知道如何修复这个错误吗?你好,Evgeny,如何为屏幕右侧的图像设置环绕图像的文本流
?非常感谢你的回答。你好,Ronen。我在理解“环绕图像文本”问题的整个概念方面遇到了巨大的困难。你能告诉我从哪里可以获得有关如何编写此类代码的信息吗?我想学习如何自己编写代码,而不仅仅是复制代码。Hi@Ramona也许可以看看这个库:您好,非常感谢您提供的信息。您知道如何通过将图像放在屏幕右侧来实现这一点吗?
ViewTreeObserver headerViewTreeObserver = _headerView.getViewTreeObserver();
headerViewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
String descriptionText = _descriptionView.getText().toString();
SpannableString spannableDescriptionText = new SpannableString(descriptionText);
LeadingMarginSpan wrapHeaderSpan = new WrapViewSpan(_headerView, _descriptionView, 12);
spannableDescriptionText.setSpan(
wrapHeaderSpan,
0,
spannableDescriptionText.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
);
_descriptionView.setText(spannableDescriptionText);
ViewTreeObserver headerViewTreeObserver = _headerView.getViewTreeObserver();
headerViewTreeObserver.removeOnGlobalLayoutListener(this);
}
});
class LeadingSpan(private val line: Int, private val margin: Int) : LeadingMarginSpan.LeadingMarginSpan2 {
override fun drawLeadingMargin(canvas: Canvas?, paint: Paint?, x: Int, dir: Int, top: Int, baseline: Int, bottom: Int, text: CharSequence?, start: Int, end: Int, first: Boolean, layout: Layout?) {}
override fun getLeadingMargin(first: Boolean): Int = if (first) margin else 0
override fun getLeadingMarginLineCount(): Int = line
}
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/about_desc"
android:text="@string/about_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
val about = view.findViewById<TextView>(R.id.about_desc)
val logoImage = ContextCompat.getDrawable(view.context, R.mipmap.ic_launcher) as Drawable
@Suppress("DEPRECATION")
view.findViewById<AppCompatImageView>(R.id.logo).setBackgroundDrawable(logoImage)
val spannableString = SpannableString(about.text)
spannableString.setSpan(Helpers.LeadingSpan(5, logoImage.intrinsicWidth + 10), 0, spannableString.length, 0)
about.text = spannableString