Können Sie mir bitte sagen, ob es eine Möglichkeit gibt, Text um ein Bild herum zu gestalten? So was:
------ text text text
| | text text text
----- text text text
text text text text
text text text text
Ich habe eine Antwort von einem Android-Entwickler zu dieser Frage erhalten. Aber ich bin mir nicht sicher, was er mit meiner eigenen Version von TextView meint? Vielen Dank für alle Tipps.
Am Montag, den 8. Februar 2010 um 23:05 Uhr schrieb Romain Guy:
Hallo,
Dies ist nicht nur mit den mitgelieferten Widgets und Layouts möglich. Sie können dazu Ihre eigene Version von TextView schreiben, es sollte nicht schwer sein.
android
android-layout
Silverburgh
quelle
quelle
Antworten:
Jetzt ist es möglich, aber nur für Telefone mit einer Version höher oder gleich 2.2, die
android.text.style.LeadingMarginSpan.LeadingMarginSpan2
in API 8 verfügbare Schnittstelle zu verwenden.Hier ist der Artikel , allerdings nicht auf Englisch, aber Sie können den Quellcode des Beispiels direkt von hier herunterladen .
Wenn Sie Ihre Anwendung mit älteren Geräten kompatibel machen möchten, können Sie ein anderes Layout ohne schwebenden Text anzeigen. Hier ist ein Beispiel:
Layout (Standard für ältere Versionen, wird für neuere Versionen programmgesteuert geändert)
<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>
Die Helferklasse
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; } }
Die MyLeadingMarginSpan2-Klasse (aktualisiert, um API 21 zu unterstützen)
public class MyLeadingMarginSpan2 implements LeadingMarginSpan2 { private int margin; private int lines; private boolean wasDrawCalled = false; private int drawLineCount = 0; public MyLeadingMarginSpan2(int lines, int margin) { this.margin = margin; this.lines = lines; } @Override public int getLeadingMargin(boolean first) { boolean isFirstMargin = first; // a different algorithm for api 21+ if (Build.VERSION.SDK_INT >= 21) { this.drawLineCount = this.wasDrawCalled ? this.drawLineCount + 1 : 0; this.wasDrawCalled = false; isFirstMargin = this.drawLineCount <= this.lines; } return isFirstMargin ? this.margin : 0; } @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) { this.wasDrawCalled = true; } @Override public int getLeadingMarginLineCount() { return this.lines; } }
Anwendungsbeispiel
ImageView thumbnailView = (ImageView) findViewById(R.id.thumbnail_view); TextView messageView = (TextView) findViewById(R.id.message_view); String text = getString(R.string.text); Display display = getWindowManager().getDefaultDisplay(); FlowTextHelper.tryFlowText(text, thumbnailView, messageView, display);
So sieht die Anwendung auf dem Android 2.2-Gerät aus:
Und das ist für das Android 2.1-Gerät:
quelle
Html.fromHtml
funktioniert nicht mit HTML, sondern unterstützt nur einfaches HTML mit wenigen Tags.Heutzutage können Sie die Bibliothek verwenden: https://github.com/deano2390/FlowTextView . So was:
<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>
quelle
Hier ist eine Verbesserung für den FlowTextHelper (aus der Antwort von vorrtex). Ich habe die Möglichkeit hinzugefügt, zusätzliche Auffüllungen zwischen Text und Bild hinzuzufügen, und die Linienberechnung verbessert, um auch die Auffüllungen zu berücksichtigen. Genießen!
public class FlowTextHelper { private static boolean mNewClassAvailable; /* class initialization fails when this throws an exception */ static { try { Class.forName("android.text.style.LeadingMarginSpan$LeadingMarginSpan2"); mNewClassAvailable = true; } catch (Exception ex) { mNewClassAvailable = false; } } public static void tryFlowText(String text, View thumbnailView, TextView messageView, Display display, int addPadding){ // 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() + addPadding; messageView.measure(width, height); //to allow getTotalPaddingTop int padding = messageView.getTotalPaddingTop(); float textLineHeight = messageView.getPaint().getTextSize(); // Set the span according to the number of lines and width of the image int lines = (int)Math.round((height - padding) / textLineHeight); SpannableString ss = new SpannableString(text); //For an html text you can use this line: SpannableStringBuilder ss = (SpannableStringBuilder)Html.fromHtml(text); ss.setSpan(new MyLeadingMarginSpan2(lines, width), 0, ss.length(), 0); 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; } }
quelle
Diese Frage scheint mit meiner Frage übereinzustimmen, wie man die leeren Stellen mit Inhalten unter dem Bild in Android füllt
Ich habe die Lösung mithilfe der Flowtext-Bibliothek gefunden. Bitte finden Sie die erste Antwort, die Ihnen bisher helfen könnte
quelle
Die Antworten von Vorrtex und Ronen funktionieren bis auf ein Detail für mich: Nach dem Umwickeln des Bildes mit Text gab es einen seltsamen "negativen" Rand unter dem Bild und auf der gegenüberliegenden Seite. Ich habe herausgefunden, dass ich mich beim Einstellen der Spanne auf dem SpannableString geändert habe
ss.setSpan(new MyLeadingMarginSpan2(lines, width), 0, ss.length(), 0);
zu
ss.setSpan(new MyLeadingMarginSpan2(lines, width), 0, lines, 0);
das stoppte die Spanne nach dem Bild. Könnte nicht in allen Fällen notwendig sein, dachte aber, ich würde teilen.
quelle
"Aber ich bin nicht sicher, was er mit meiner eigenen Version von TextView meint?"
Er bedeutet, dass Sie die Klasse android.widget.TextView (oder Canvas oder eine andere renderbare Oberfläche) erweitern und Ihre eigene überschreibende Version implementieren können, die eingebettete Bilder mit umlaufendem Text ermöglicht.
Dies kann eine Menge Arbeit sein, je nachdem, wie allgemein Sie es machen.
quelle
Ich kann einen komfortableren Konstruktor für die MyLeadingMarginSpan2-Klasse anbieten
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; }
quelle
text flow around image
Bild auf der rechten Seite des Bildschirms ein? Antwort sehr geschätzt.Die Antwort von vorrtex hat bei mir nicht funktioniert, aber ich habe viel davon genommen und meine eigene Lösung gefunden. Hier ist es:
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) { } }
und in meiner eigentlichen Klasse, in der die Spanne verwendet wird:
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); } });
Ich brauchte den globalen Layout-Listener, um die richtigen Werte für
getWidth()
und zu erhaltengetHeight()
.Hier ist das Ergebnis:
quelle
Versuchen Sie diese einfache Implementierung mit Kotlin und AndroidX. Erstellen Sie zunächst eine führende Span-Helferklasse:
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 }
Erstellen Sie dann ein Layout mit
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>
und schließlich in Ihrem
activity
oderfragment
wie einrichten: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
Ändern Sie die Nummer 5
Helpers.LeadingSpan(5, logoImage.intrinsicWidth + 10)
entsprechend Ihrer Zeichenhöhe.quelle