Android için TextView'i otomatik sığdır


231

Arka fon

Çoğu zaman TextView yazı tipini kendiliğinden verilen sınırlara sığdırmamız gerekir.

Sorun

Ne yazık ki, bu sorun hakkında konuşan çok sayıda konu ve gönderi (ve önerilen çözümler) olmasına rağmen (örnek burada , burada ve burada ), hiçbiri gerçekten iyi çalışmıyor.

Bu yüzden, gerçek bir anlaşma bulana kadar her birini test etmeye karar verdim.

Böyle bir textView gelen gereksinimleri olması gerektiğini düşünüyorum:

  1. Herhangi bir yazı tipi, yazı tipi, stil ve karakter kümesinin kullanılmasına izin vermelidir.

  2. Hem genişliği hem de yüksekliği ele almalıdır

  3. Metin sınırlama nedeniyle sığmadığı sürece kesilmez, buna verdik (örnek: çok uzun metin, çok küçük boyut). Ancak, sadece bu durumlar için yatay / dikey kaydırma çubuğu talep edebiliriz.

  4. Çok satırlı veya tek satırlı olmalıdır. Çok hatlı olması durumunda, maksimum ve minimum hatlara izin verin.

  5. Hesaplamada yavaş olmamalıdır. En iyi boyutu bulmak için bir döngü mü kullanıyorsunuz? En azından optimize edin ve her seferinde örneklemenizi 1 arttırmayın.

  6. Çok satırlı olması durumunda, yeniden boyutlandırmayı veya daha fazla satır kullanmayı tercih etmeli ve / veya "\ n" karakterini kullanarak satırları kendimiz seçmemize izin vermelidir.

Ne denedim

Çok fazla örnek denedim (bağlantılarınkiler dahil, hakkında yazdım) ve ayrıca vakaları ele almak için bunları değiştirmeye çalıştım, konuştum, ama hiçbiri gerçekten işe yaramıyor.

Görsel olarak TextView otomatik olarak uygun olup olmadığını görmek için izin veren örnek bir proje yaptım.

Şu anda, örnek projem sadece metni (İngilizce alfabe artı rakamlar) ve textView boyutunu rastgele ve tek satırda kalmasına izin, ancak bu bile denedim herhangi bir örnek üzerinde iyi çalışmıyor.

İşte kod ( burada da mevcuttur ):

Dosya res/layout/activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
  android:layout_height="match_parent" tools:context=".MainActivity">
  <Button android:id="@+id/button1" android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_centerHorizontal="true" android:text="Button" />
  <FrameLayout android:layout_width="match_parent"
    android:layout_height="wrap_content" android:layout_above="@+id/button1"
    android:layout_alignParentLeft="true" android:background="#ffff0000"
    android:layout_alignParentRight="true" android:id="@+id/container"
    android:layout_alignParentTop="true" />

</RelativeLayout>

Dosya src/.../MainActivity.java

public class MainActivity extends Activity
  {
  private final Random        _random            =new Random();
  private static final String ALLOWED_CHARACTERS ="qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";

  @Override
  protected void onCreate(final Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final ViewGroup container=(ViewGroup)findViewById(R.id.container);
    findViewById(R.id.button1).setOnClickListener(new OnClickListener()
      {
        @Override
        public void onClick(final View v)
          {
          container.removeAllViews();
          final int maxWidth=container.getWidth();
          final int maxHeight=container.getHeight();
          final FontFitTextView fontFitTextView=new FontFitTextView(MainActivity.this);
          final int width=_random.nextInt(maxWidth)+1;
          final int height=_random.nextInt(maxHeight)+1;
          fontFitTextView.setLayoutParams(new LayoutParams(width,height));
          fontFitTextView.setSingleLine();
          fontFitTextView.setBackgroundColor(0xff00ff00);
          final String text=getRandomText();
          fontFitTextView.setText(text);
          container.addView(fontFitTextView);
          Log.d("DEBUG","width:"+width+" height:"+height+" text:"+text);
          }
      });
    }

  private String getRandomText()
    {
    final int textLength=_random.nextInt(20)+1;
    final StringBuilder builder=new StringBuilder();
    for(int i=0;i<textLength;++i)
      builder.append(ALLOWED_CHARACTERS.charAt(_random.nextInt(ALLOWED_CHARACTERS.length())));
    return builder.toString();
    }
  }

Soru

Gerçekten çalışan bu ortak soruna bir çözüm bilen var mı?

Yazdıklarımın çok daha az özelliğine sahip bir çözüm bile, örneğin sabit sayıda metin satırı olan ve yazı tipini boyutuna göre ayarlayan, ancak asla garip aksaklıklara ve metnin elde edilmesine sahip olan bir çözüm bile kullanılabilir alanla karşılaştırıldığında büyük / küçük.


GitHub projesi

Bu kadar önemli bir TextView'un olduğundan, herkesin kolayca kullanabilir ve buna katkıda bulunabilir, böylece bir kütüphane yayınlamaya karar verdik burada .



@Thrakbad bahsettiğim bağlantılardan biri. Ayrıca testi geçemez.
android geliştirici

Ah üzgünüm, son örneği bir şekilde özledim
Thrakbad

Evet, lütfen bana inanın, birçok örnek denedim ve bulduğum sorunları düzeltmek için bunları değiştirmeye çalıştım, ancak asla başarılı olamadım. Çalışabileceğini düşündüğünüz bir şey bulursanız lütfen test edin. Sadece bunun için bir örnek kod gönderdim.
android geliştirici

1
@ kural bu zaten okudum gönderilerden biridir ve tüm kod örnekleri test ettik. ayrıca bence çift yayınladın.
android geliştirici

Yanıtlar:


147

MartinH en basit düzeltme sayesinde burada , bu kod da ilgilenir android:drawableLeft, android:drawableRight, android:drawableTopve android:drawableBottometiketleri.


Buradaki cevabım sizi Sınırlar İçinde Sığacak Otomatik Ölçekli TextView Metnini mutlu etmelidir

Test durumunuzu değiştirdim:

@Override
protected void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final ViewGroup container = (ViewGroup) findViewById(R.id.container);
    findViewById(R.id.button1).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(final View v) {
            container.removeAllViews();
            final int maxWidth = container.getWidth();
            final int maxHeight = container.getHeight();
            final AutoResizeTextView fontFitTextView = new AutoResizeTextView(MainActivity.this);
            final int width = _random.nextInt(maxWidth) + 1;
            final int height = _random.nextInt(maxHeight) + 1;
            fontFitTextView.setLayoutParams(new FrameLayout.LayoutParams(
                    width, height));
            int maxLines = _random.nextInt(4) + 1;
            fontFitTextView.setMaxLines(maxLines);
            fontFitTextView.setTextSize(500);// max size
            fontFitTextView.enableSizeCache(false);
            fontFitTextView.setBackgroundColor(0xff00ff00);
            final String text = getRandomText();
            fontFitTextView.setText(text);
            container.addView(fontFitTextView);
            Log.d("DEBUG", "width:" + width + " height:" + height
                    + " text:" + text + " maxLines:" + maxLines);
        }
    });
}

Android geliştiricisinin isteğine göre burada kod gönderiyorum :

Son etki:

Resim açıklamasını buraya girin

Örnek Düzen dosyası:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp" >

<com.vj.widgets.AutoResizeTextView
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:ellipsize="none"
    android:maxLines="2"
    android:text="Auto Resized Text, max 2 lines"
    android:textSize="100sp" /> <!-- maximum size -->

<com.vj.widgets.AutoResizeTextView
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:ellipsize="none"
    android:gravity="center"
    android:maxLines="1"
    android:text="Auto Resized Text, max 1 line"
    android:textSize="100sp" /> <!-- maximum size -->

<com.vj.widgets.AutoResizeTextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Auto Resized Text"
    android:textSize="500sp" /> <!-- maximum size -->

</LinearLayout>

Ve Java kodu:

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.RectF;
import android.os.Build;
import android.text.Layout.Alignment;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.widget.TextView;

public class AutoResizeTextView extends TextView {
    private interface SizeTester {
        /**
         *
         * @param suggestedSize
         *            Size of text to be tested
         * @param availableSpace
         *            available space in which text must fit
         * @return an integer < 0 if after applying {@code suggestedSize} to
         *         text, it takes less space than {@code availableSpace}, > 0
         *         otherwise
         */
        public int onTestSize(int suggestedSize, RectF availableSpace);
    }

    private RectF mTextRect = new RectF();

    private RectF mAvailableSpaceRect;

    private SparseIntArray mTextCachedSizes;

    private TextPaint mPaint;

    private float mMaxTextSize;

    private float mSpacingMult = 1.0f;

    private float mSpacingAdd = 0.0f;

    private float mMinTextSize = 20;

    private int mWidthLimit;

    private static final int NO_LINE_LIMIT = -1;
    private int mMaxLines;

    private boolean mEnableSizeCache = true;
    private boolean mInitializedDimens;

    public AutoResizeTextView(Context context) {
        super(context);
        initialize();
    }

    public AutoResizeTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }

    public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initialize();
    }

    private void initialize() {
        mPaint = new TextPaint(getPaint());
        mMaxTextSize = getTextSize();
        mAvailableSpaceRect = new RectF();
        mTextCachedSizes = new SparseIntArray();
        if (mMaxLines == 0) {
            // no value was assigned during construction
            mMaxLines = NO_LINE_LIMIT;
        }
    }

    @Override
    public void setTextSize(float size) {
        mMaxTextSize = size;
        mTextCachedSizes.clear();
        adjustTextSize();
    }

    @Override
    public void setMaxLines(int maxlines) {
        super.setMaxLines(maxlines);
        mMaxLines = maxlines;
        adjustTextSize();
    }

    public int getMaxLines() {
        return mMaxLines;
    }

    @Override
    public void setSingleLine() {
        super.setSingleLine();
        mMaxLines = 1;
        adjustTextSize();
    }

    @Override
    public void setSingleLine(boolean singleLine) {
        super.setSingleLine(singleLine);
        if (singleLine) {
            mMaxLines = 1;
        } else {
            mMaxLines = NO_LINE_LIMIT;
        }
        adjustTextSize();
    }

    @Override
    public void setLines(int lines) {
        super.setLines(lines);
        mMaxLines = lines;
        adjustTextSize();
    }

    @Override
    public void setTextSize(int unit, float size) {
        Context c = getContext();
        Resources r;

        if (c == null)
            r = Resources.getSystem();
        else
            r = c.getResources();
        mMaxTextSize = TypedValue.applyDimension(unit, size,
                r.getDisplayMetrics());
        mTextCachedSizes.clear();
        adjustTextSize();
    }

    @Override
    public void setLineSpacing(float add, float mult) {
        super.setLineSpacing(add, mult);
        mSpacingMult = mult;
        mSpacingAdd = add;
    }

    /**
     * Set the lower text size limit and invalidate the view
     *
     * @param minTextSize
     */
    public void setMinTextSize(float minTextSize) {
        mMinTextSize = minTextSize;
        adjustTextSize();
    }

    private void adjustTextSize() {
        if (!mInitializedDimens) {
            return;
        }
        int startSize = (int) mMinTextSize;
        int heightLimit = getMeasuredHeight() - getCompoundPaddingBottom()
                - getCompoundPaddingTop();
        mWidthLimit = getMeasuredWidth() - getCompoundPaddingLeft()
                - getCompoundPaddingRight();
        mAvailableSpaceRect.right = mWidthLimit;
        mAvailableSpaceRect.bottom = heightLimit;
        super.setTextSize(
                TypedValue.COMPLEX_UNIT_PX,
                efficientTextSizeSearch(startSize, (int) mMaxTextSize,
                        mSizeTester, mAvailableSpaceRect));
    }

    private final SizeTester mSizeTester = new SizeTester() {
        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
        @Override
        public int onTestSize(int suggestedSize, RectF availableSPace) {
            mPaint.setTextSize(suggestedSize);
            String text = getText().toString();
            boolean singleline = getMaxLines() == 1;
            if (singleline) {
                mTextRect.bottom = mPaint.getFontSpacing();
                mTextRect.right = mPaint.measureText(text);
            } else {
                StaticLayout layout = new StaticLayout(text, mPaint,
                        mWidthLimit, Alignment.ALIGN_NORMAL, mSpacingMult,
                        mSpacingAdd, true);

                // Return early if we have more lines
                if (getMaxLines() != NO_LINE_LIMIT
                        && layout.getLineCount() > getMaxLines()) {
                    return 1;
                }
                mTextRect.bottom = layout.getHeight();
                int maxWidth = -1;
                for (int i = 0; i < layout.getLineCount(); i++) {
                    if (maxWidth < layout.getLineWidth(i)) {
                        maxWidth = (int) layout.getLineWidth(i);
                    }
                }
                mTextRect.right = maxWidth;
            }

            mTextRect.offsetTo(0, 0);
            if (availableSPace.contains(mTextRect)) {

                // May be too small, don't worry we will find the best match
                return -1;
            } else {
                // too big
                return 1;
            }
        }
    };

    /**
     * Enables or disables size caching, enabling it will improve performance
     * where you are animating a value inside TextView. This stores the font
     * size against getText().length() Be careful though while enabling it as 0
     * takes more space than 1 on some fonts and so on.
     *
     * @param enable
     *            Enable font size caching
     */
    public void enableSizeCache(boolean enable) {
        mEnableSizeCache = enable;
        mTextCachedSizes.clear();
        adjustTextSize(getText().toString());
    }

    private int efficientTextSizeSearch(int start, int end,
            SizeTester sizeTester, RectF availableSpace) {
        if (!mEnableSizeCache) {
            return binarySearch(start, end, sizeTester, availableSpace);
        }
        int key = getText().toString().length();
        int size = mTextCachedSizes.get(key);
        if (size != 0) {
            return size;
        }
        size = binarySearch(start, end, sizeTester, availableSpace);
        mTextCachedSizes.put(key, size);
        return size;
    }

    private static int binarySearch(int start, int end, SizeTester sizeTester,
            RectF availableSpace) {
        int lastBest = start;
        int lo = start;
        int hi = end - 1;
        int mid = 0;
        while (lo <= hi) {
            mid = (lo + hi) >>> 1;
            int midValCmp = sizeTester.onTestSize(mid, availableSpace);
            if (midValCmp < 0) {
                lastBest = lo;
                lo = mid + 1;
            } else if (midValCmp > 0) {
                hi = mid - 1;
                lastBest = hi;
            } else {
                return mid;
            }
        }
        // Make sure to return the last best.
        // This is what should always be returned.
        return lastBest;

    }

    @Override
    protected void onTextChanged(final CharSequence text, final int start,
            final int before, final int after) {
        super.onTextChanged(text, start, before, after);
        adjustTextSize();
    }

    @Override
    protected void onSizeChanged(int width, int height, int oldwidth,
            int oldheight) {
        mInitializedDimens = true;
        mTextCachedSizes.clear();
        super.onSizeChanged(width, height, oldwidth, oldheight);
        if (width != oldwidth || height != oldheight) {
            adjustTextSize();
        }
    }
}

Uyarı:

Gerçi Android 3.1 (Honeycomb) bu çözülmüş hata dikkat edin .


Tamam, kodu test ettim ve iyi görünüyor. ancak, API 16 için olan getMaxLines () yöntemini kullandınız, ancak maxLines'i depolayabilir ve setMaxLines'i geçersiz kılarak ve değerini depolayarak alabilirsiniz. bunu değiştirdim ve şimdi iyi çalışıyor. Ayrıca, bazen metin en küçük boyut için bile çok uzun olabileceğinden, setEllipsize kullanmaya çalıştım ve bu da işe yaradı! Bir kazananımız olduğunu düşünüyorum. Lütfen kodunuzu buraya gönderin, böylece doğru olanı işaretleyebilirim.
android geliştirici

getMaxLines () öğesini kaldırmadınız ve kendinizinkini kullandınız. yine de yalnızca API16'dan çalışacaktır ... lütfen herkesin kullanabilmesi için değiştirin. Parametreyi bir alana depolamak ve sonra getMaxLines kullanmak yerine alana erişmek için setMaxLines'i geçersiz kılmayı öneririm.
android geliştirici

şimdi kontrol edin, maksimum hat desteği eklendi.
M-WaJeEh

iyi görünüyor. onTestSize () yöntemine "@TargetApi (Build.VERSION_CODES.JELLY_BEAN)" yöntemini eklemelisiniz, böylece Lint bu konuda bir hata / uyarı vermeyecek ve bunun yerine CTOR'da başlatacaktır. ikili aramayı statik olarak ayarlamak da iyi bir şeydir ve en büyük CTOR'da başlatmayı yapabilir ve diğer CTOR'lardan çağırabilirsiniz. ancak, bu gerçekten en iyi cevap ve bir V'yi hak ediyorsunuz. bu nihayet bu eski soru için iyi bir cevap. aferin!
android geliştirici

@ M-WaJeEh bu konudaki yeni gönderiyi kontrol et. "MartinH" adlı bir kullanıcı, kodunuz için bir düzeltme olduğunu söylüyor.
android geliştirici

14

M-WaJeEh'nin cevabını, yanlardaki bileşik çekmeceleri dikkate almak için biraz değiştirdim.

getCompoundPaddingXXXX()Yöntemler dönmek padding of the view + drawable space. Örnek için bakınız: TextView.getCompoundPaddingLeft ()

Sorun: Bu, metin için kullanılabilen TextView alanının genişlik ve yüksekliğinin ölçümünü düzeltir. Çekilebilir boyutu dikkate almazsak, yok sayılır ve metin çekilebilir ile çakışır.


Güncellenmiş segment adjustTextSize(String):

private void adjustTextSize(final String text) {
  if (!mInitialized) {
    return;
  }
  int heightLimit = getMeasuredHeight() - getCompoundPaddingBottom() - getCompoundPaddingTop();
  mWidthLimit = getMeasuredWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight();

  mAvailableSpaceRect.right = mWidthLimit;
  mAvailableSpaceRect.bottom = heightLimit;

  int maxTextSplits = text.split(" ").length;
  AutoResizeTextView.super.setMaxLines(Math.min(maxTextSplits, mMaxLines));

  super.setTextSize(
      TypedValue.COMPLEX_UNIT_PX,
      binarySearch((int) mMinTextSize, (int) mMaxTextSize,
                   mSizeTester, mAvailableSpaceRect));
}

M-WaJeEh kaynağını güncellediniz. neden yeni bir cevap olarak koydun? iyi niyetiniz olduğunu biliyorum, ama cevap olarak koymak iyi bir şey mi? cevabını fark edebildiğinden emin değilim ...
android geliştirici

1
Bu güncellemeyi sağlayabilmek için yeni kaydoldum. İtibar sayım sadece 1'dir ve bu da benim olmayan gönderiler hakkında yorum yapmamı engeller (minimum 15).
MartinH

Endişelenmeyin, belki de şu anda onunla iletişim kuramadığım için cevabımı M-WajeEh'e iletmek istersiniz: /
MartinH

Lütfen ne yaptığını daha iyi anlatmaya çalışabilir misin? bir hatayı düzelttin mi? hatayı göstermek için bir ekran görüntüsü gösterebilir misiniz?
android geliştirici

1
Merhaba @ MartinH, SO hoş geldiniz. Somurtkan bir şekilde bunu inceleyeceğim ve adınızı söyleyerek ve testten sonra gönderinize bağlantı göndererek cevabımı güncelleyeceğim . Bana sadece birkaç gün ver lütfen. Bugünlerde başka bir yere takıldım.
M-WaJeEh

7

Tamam Geçen hafta testinize tam olarak uyacak şekilde kodumu büyük ölçüde yeniden yazmak için kullandım . Şimdi bu 1: 1'i kopyalayabilirsiniz ve hemen çalışır - dahil setSingleLine(). Lütfen ayarlamayı MIN_TEXT_SIZEve MAX_TEXT_SIZEuç değerlere gidip gitmediğinizi unutmayın .

Yakınsama algoritması şuna benzer:

for (float testSize; (upperTextSize - lowerTextSize) > mThreshold;) {

    // Go to the mean value...
    testSize = (upperTextSize + lowerTextSize) / 2;

    // ... inflate the dummy TextView by setting a scaled textSize and the text...
    mTestView.setTextSize(TypedValue.COMPLEX_UNIT_SP, testSize / mScaledDensityFactor);
    mTestView.setText(text);

    // ... call measure to find the current values that the text WANTS to occupy
    mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
    int tempHeight = mTestView.getMeasuredHeight();

    // ... decide whether those values are appropriate.
    if (tempHeight >= targetFieldHeight) {
        upperTextSize = testSize; // Font is too big, decrease upperSize
    }
    else {
        lowerTextSize = testSize; // Font is too small, increase lowerSize
    }
}

Ve tüm sınıf burada bulunabilir.

Sonuç şimdi çok esnektir. Bu, xml'de bildirilenle aynı şekilde çalışır:

<com.example.myProject.AutoFitText
    android:id="@+id/textView"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="4"
    android:text="@string/LoremIpsum" />

... ve testinizdeki gibi programlı bir şekilde oluşturuldu.

Umarım bunu şimdi kullanabilirsiniz. Bu setText(CharSequence text)arada kullanmak için şimdi arayabilirsiniz . Sınıf muazzam derecede nadir istisnalarla ilgilenir ve kaya gibi sağlam olmalıdır. Algoritma olmadığını tek şey değil henüz desteklemek geçerli:

  • setMaxLines(x)Nereye ararx >= 2

Ama isterseniz bunu inşa etmenize yardımcı olacak kapsamlı yorumlar ekledim!


Lütfen aklınızda bulundurun:

Bunu normalde tek bir satırla sınırlamadan kullanırsanız, daha önce de belirttiğiniz gibi kelime kırma olabilir. Bu bir Android özelliği , hatası değilAutoFitText . Android her zaman TextView için çok uzun olan kelimeleri kıracak ve aslında oldukça kolay. Buraya müdahale etmek istiyorsanız lütfen 203 satırından başlayarak yorumlarımı ve kodumu görün. Yeterli bir bölünme ve sizin için tanıma zaten yazdım, bundan sonra yapmanız gereken tek şey kelimeleri ayırmak ve sonra istediğiniz gibi değiştirmek .

Sonuç olarak: Alan karakterlerini de desteklemek için testinizi yeniden yazmayı düşünmelisiniz, örneğin:

final Random _random = new Random();
final String ALLOWED_CHARACTERS = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
final int textLength = _random.nextInt(80) + 20;
final StringBuilder builder = new StringBuilder();
for (int i = 0; i < textLength; ++i) {
    if (i % 7 == 0 && i != 0) {
        builder.append(" ");
    }
    builder.append(ALLOWED_CHARACTERS.charAt(_random.nextInt(ALLOWED_CHARACTERS.length())));
}
((AutoFitText) findViewById(R.id.textViewMessage)).setText(builder.toString());

Bu çok güzel (ve daha gerçekçi) sonuçlar doğuracaktır.
Bu konuda da işe başlamak için yorum bulacaksınız.

İyi şanslar ve saygılarımla


bazı sorunlar var, ama genel olarak çok iyi bir iş çalışıyor. sorunlar: çok satır desteklemiyor (yazdığınız gibi) ve bunu yapmaya çalışmanız durumunda kelimeyi kıracak, tamamen önlenebileceğini düşündüğüm API16 işlevini (addOnGlobalLayoutListener) içerir (veya en azından OnPreDrawListener kullanın), kullanır İkili arama, hiç sığmadığı boyutları test edebilir (ve hiçbir sebep olmadan daha fazla bellek kullanır, bu da çok büyükse istisnalara neden olabilir), bir şey değiştiğinde (metnin kendisi gibi) işlemeyi desteklemez.
android geliştirici

btw, mTestView değişkenini kullanmanız ve testi doğrudan geçerli görünümde yapmanız gerekmez ve bu şekilde tüm özel şeyleri dikkate almanız gerekmez. Ayrıca, MAX_TEXT_SIZE yerine targetFieldHeight kullanabilirsiniz düşünüyorum.
android geliştirici

Saf bir ikili arama (özellikle 2 tanesi) yerine, hedefin ölçülen boyuta oranıyla daha iyi bir tahmin alabileceğinizi düşünüyorum. Ayrıca başka yöntemler (newton ve hatırlamıyorum başka bir şey) vardır ama onlar burada uygun sanmıyorum. şimdi önerdiğim daha iyidir çünkü maksimum boyutu test etmek yerine min boyutundan başlayabilirsiniz. gerçek, daha iyi bir tahmin yapmaya devam ettiğiniz için maksimum boyuta ihtiyacınız olmayacağıdır.
android geliştirici

Ayrıca koşul sadece olabilir 2 ikili arama tek bir satıra birleştirilebilir düşünüyorum: if (getMeasuredWidth ()> = targetFieldWidth || getMeasuredHeight ()> = targetFieldHeight)
android geliştirici

1
bulduğum başka bir konu metin yerçekimi. bence dikey olarak ortalanamaz.
android geliştirici

4

Benim şartım

  • ScalableTextView öğesini tıklayın
  • ListActivity'yi açın ve çeşitli uzunluktaki dize öğelerini görüntüleyin.
  • Listeden bir metin seçin.
  • Başka bir etkinlikte metni tekrar ScalableTextView öğesine ayarlayın.

Bağlantıya başvurdum : Otomatik Ölçekli TextView Metni Sınırlar İçine Sığacak (yorumlar dahil) ve ayrıca DialogTitle.java

Sağlanan çözümün güzel ve basit olduğunu, ancak metin kutusunun boyutunu dinamik olarak değiştirmediğini buldum. Liste görünümünden seçilen metin uzunluğu, içindeki mevcut metin uzunluğundan daha büyük olduğunda harika çalışır ScalableTextView. Uzunluğu, metinde varolan metinden daha küçük olan metin seçildiğinde, metnin ScalableTextViewboyutunu büyütmez, metni daha küçük boyutta gösterir.

Metin boyutunu temel alarak metin boyutunu yeniden ayarlamak için ScalableTextView.java değiştirdim. İşte benimScalableTextView.java

public class ScalableTextView extends TextView
{
float defaultTextSize = 0.0f;

public ScalableTextView(Context context, AttributeSet attrs, int defStyle)
{
    super(context, attrs, defStyle);
    setSingleLine();
    setEllipsize(TruncateAt.END);
    defaultTextSize = getTextSize();
}

public ScalableTextView(Context context, AttributeSet attrs)
{
    super(context, attrs);
    setSingleLine();
    setEllipsize(TruncateAt.END);
    defaultTextSize = getTextSize();
}

public ScalableTextView(Context context)
{
    super(context);
    setSingleLine();
    setEllipsize(TruncateAt.END);
    defaultTextSize = getTextSize();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
    setTextSize(TypedValue.COMPLEX_UNIT_PX, defaultTextSize);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    final Layout layout = getLayout();
    if (layout != null)
    {
        final int lineCount = layout.getLineCount();
        if (lineCount > 0)
        {
            int ellipsisCount = layout.getEllipsisCount(lineCount - 1);
            while (ellipsisCount > 0)
            {
                final float textSize = getTextSize();

                // textSize is already expressed in pixels
                setTextSize(TypedValue.COMPLEX_UNIT_PX, (textSize - 1));

                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
                ellipsisCount = layout.getEllipsisCount(lineCount - 1);
            }
        }
    }
}
}

Mutlu Kodlama ....


Her yineleme için bir azaltmak yerine ikili aramayı kullanarak yapmamalısınız? Ayrıca, bu kod, TextView komutunu birden çok satıra sahip olmak yerine tek bir metin satırına sahip olmaya zorlar.
android geliştirici

Benim için işe yarayan tek çözüm buydu. Benim sorunum sadece 4.1 4.1 görünümü uymayan TExtView ile ilgili
FOliveira

% 100 ebeveyne uymuyor gibi görünüyor. ekran görüntüsüne bakın: s14.postimg.org/93c2xgs75/Screenshot_2.png
user25

4

Bu özelliğin android sürümlerini adım adım nasıl çalıştığını açıklayacağım:

1- Proje destek dosyasına 26.xx android destek kütüphanesini aktarın. IDE'de destek kitaplığı yoksa, otomatik olarak indirilir.

dependencies {
    compile 'com.android.support:support-v4:26.1.0'
    compile 'com.android.support:appcompat-v7:26.1.0'
    compile 'com.android.support:support-v13:26.1.0' }

allprojects {
    repositories {
        jcenter()
        maven {
            url "https://maven.google.com"
        }
    } }

2- XMLView düzeninizi açın ve bu etiketi yeniden düzenleyin. Bu senaryo şudur: sistemdeki yazı tipi boyutu dahil edildiğinde, metni sözcük kaydırmaya değil, uygun genişliğe sığdırın.

<android.support.v7.widget.AppCompatTextView
            android:id="@+id/textViewAutoSize"
            android:layout_width="match_parent"
            android:layout_height="25dp"
            android:ellipsize="none"
            android:text="Auto size text with compatible lower android versions."
            android:textSize="12sp"
            app:autoSizeMaxTextSize="14sp"
            app:autoSizeMinTextSize="4sp"
            app:autoSizeStepGranularity="0.5sp"
            app:autoSizeTextType="uniform" />

Ancak otomatik sığdırılmış TextView uygulaması iyi çalışmaz. Yazı tipi boyutunu yeniden boyutlandırmak yerine, metnin bir sonraki satıra kötü bir şekilde sarıldığı zamanlar var ... İyi bir şeymiş gibi videolarında bile gösteriliyor: youtu.be/fjUdJ2aVqE4?t=14 . "TextView" öğesinin "w" değerinin bir sonraki satıra nasıl gittiğine dikkat edin ... Her neyse, burada yeni bir istek oluşturdu: issuetracker.google.com/issues/68787108 . Lütfen oynamayı düşünün.
android geliştirici

@androiddeveloper Haklısın. Bu etiket uygulama tek satır çalışır açıklamak vermedi. Sarma çizgilerine ihtiyacınız varsa, şu özelliği değiştirmeniz gerekir: android: layout_height = "wrap_content". Ancak android.support.v7.widget.AppCompatTextView ve sınıf uygulama garantisi bu niteliği yerine getirir.
Sinan Ergin

Anladığını sanmıyorum. Bunu çözmenin başka bir yolu yoksa, parça kelimelerin bir sonraki satıra sarılmasını istemiyorum. Videoda, bunun yerine yazı tiplerinin daha küçük olabileceğini kolayca görebilirsiniz. Sorun bu. Bunu nasıl düzeltebileceğini biliyor musun? Yani kelime kaydırma olmayacak ve sadece yazı tipi boyutu değişecek mi?
android geliştirici

@androiddeveloper Birçok senaryo var. Benim senaryom: autofit metin ile düzeltmek ve word wrap değil. Sistem yazı tipi boyutunu incelerken, metnin metin görünümünde sözcük kaydırmayı göstermemesi gerekir. Sözcük kaydırma sizin için önemli değilse, sabit yüksekliği ayarlamak için rahatsız etmeyin. Bu özellik sadece word wrap kullanarak, metnin AutoView sabit genişliğe sahip olabilir TextView.
Sinan Ergin

Testlerime göre, yazı tipi boyutları doğru bir şekilde değişmiyor ve sözcük kaydırma sorunu oluşabilir.
android geliştirici

3

Uyarı, Android 3 (Honeycomb) ve Android 4.0'da (Ice Cream Sandwich) hata

Android sürümleri: 3.1 - 4.04 bir hata var, TextView içindeki setTextSize () sadece ilk kez çalışır (ilk çağırma).

Hata, Android 4.0 ve Sayı 17343'teki Sayı 22493: TextView yükseklik hatası'nda açıklanmaktadır : HoneyComb'ta metin boyutunu artırıp azalttıktan sonra düğmenin yüksekliği ve metni orijinal durumuna dönmüyor .

Çözüm, boyutu değiştirmeden önce TextView'a atanan metne yeni satır karakteri eklemektir:

final String DOUBLE_BYTE_SPACE = "\u3000";
textView.append(DOUBLE_BYTE_SPACE);

Aşağıdaki gibi benim kodu kullanın:

final String DOUBLE_BYTE_SPACE = "\u3000";
AutoResizeTextView textView = (AutoResizeTextView) view.findViewById(R.id.aTextView);
String fixString = "";
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR1
   && android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {  
    fixString = DOUBLE_BYTE_SPACE;
}
textView.setText(fixString + "The text" + fixString);

Bu "\ u3000" karakterini ortalamam için metnimin soluna ve sağına ekliyorum. Sola hizaladıysanız, yalnızca sağa ekleyin. Tabii ki aynı zamanda AutoResizeTextView widget ile gömülü olabilir, ancak düzeltme kodunu dışarıda tutmak istedim.


tam olarak hangi satırlarda (hangi satırlardan önce / sonra) ve hangi fonksiyonda olduğunu gösterebilir misiniz?
android geliştirici

tamam teşekkürler. Umarım bu her şeyi düzeltir ve bunu kim okursa onu yararlı bulacaktır. şu anda kontrol edemiyorum. belki bir dahaki sefere ben kullanacağım.
android geliştirici

Bu kod, görünümün kodunun dışından çağrılır. Bunun yerine, görünümün kodu içinde bununla başa çıkmanın iyi bir alternatif yolunu biliyor musunuz?
android geliştirici

textView.setText () öğesini geçersiz kılabilir ve bu kodu TextView sınıfının içine koyabilirsiniz
Malachiasz

Ayrıca, "getText ()" ifadesinin ek karakterleri içeren metni döndüreceği anlamına da gelmez mi?
android geliştirici

3

Şimdi bu sorunun resmi bir çözümü var. Android O ile sunulan TextView'leri otomatik boyutlandırma Destek Kitaplığı 26'da bulunur ve Android 4.0'a kadar geriye dönük olarak uyumludur.

https://developer.android.com/preview/features/autosizing-textview.html

Bu bilgileri de içeren https://stackoverflow.com/a/42940171/47680 neden bir yönetici tarafından silindiğinden emin değilim .


1
Evet, ben de duydum, ama ne kadar iyi? Kullanmanın bir örneği var mı? Videolarında bile, bir hata fark ettim: bir kelimeden bir mektup (sarılmış) sonra satırına geldi: youtu.be/1N9KveJ-FU8?t=781 ("W" dikkat edin)
android geliştirici

3

Haziran 2018'den itibaren Android , Android 4.0 (API seviye 14) ve üstü için bu özelliği resmi olarak destekliyor .
Android 8.0 (API seviye 26) ve üstü ile:

setAutoSizeTextTypeUniformWithConfiguration(int autoSizeMinTextSize, int autoSizeMaxTextSize, 
        int autoSizeStepGranularity, int unit);

Android 8.0'dan önceki Android sürümleri (API düzey 26):

TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(TextView textView,
int autoSizeMinTextSize, int autoSizeMaxTextSize, int autoSizeStepGranularity, int unit)

Detay cevabımı kontrol et .


1

Metin görünümünü bir görüntüye dönüştürün ve görüntüyü sınırlar içinde ölçeklendirin.

Bir görünümü bir Görüntüye nasıl dönüştüreceğinize ilişkin bir örnek: Bir görünümü Android'de göstermeden Bitmap'e dönüştürme?

Sorun şu ki, metniniz seçilemez, ancak hile yapmalıdır. Ben denemedim, bu yüzden (ölçekleme nedeniyle) nasıl görüneceğinden emin değilim.


Bu güzel bir fikir, ancak bu metin pikselli hale getirecek ve textView ile kullanabileceğim herhangi bir özelliği kaldıracak.
android geliştirici

0

Aşağıda, özel Font için ek işlevsellik içeren avalancha TextView bulunmaktadır.

Kullanımı:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:foo="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="match_parent" >

                <de.meinprospekt.androidhd.view.AutoFitText
                android:layout_width="wrap_content"
                android:layout_height="10dp"
                android:text="Small Text"
                android:textColor="#FFFFFF"
                android:textSize="100sp"
                foo:customFont="fonts/Roboto-Light.ttf" />

</FrameLayout>

Eklemeyi unutmayın: xmlns: foo = "http://schemas.android.com/apk/res-auto". Yazı tipi varlıklar şöminesinde olmalıdır

import java.util.ArrayList;
import java.util.List;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.TextView;
import de.meinprospekt.androidhd.R;
import de.meinprospekt.androidhd.adapter.BrochuresHorizontalAdapter;
import de.meinprospekt.androidhd.util.LOG;

/**
 * https://stackoverflow.com/a/16174468/2075875 This class builds a new android Widget named AutoFitText which can be used instead of a TextView to
 * have the text font size in it automatically fit to match the screen width. Credits go largely to Dunni, gjpc, gregm and speedplane from
 * Stackoverflow, method has been (style-) optimized and rewritten to match android coding standards and our MBC. This version upgrades the original
 * "AutoFitTextView" to now also be adaptable to height and to accept the different TextView types (Button, TextClock etc.)
 * 
 * @author pheuschk
 * @createDate: 18.04.2013
 * 
 * combined with: https://stackoverflow.com/a/7197867/2075875
 */
@SuppressWarnings("unused")
public class AutoFitText extends TextView {

    private static final String TAG = AutoFitText.class.getSimpleName();

    /** Global min and max for text size. Remember: values are in pixels! */
    private final int MIN_TEXT_SIZE = 10;
    private final int MAX_TEXT_SIZE = 400;

    /** Flag for singleLine */
    private boolean mSingleLine = false;

    /**
     * A dummy {@link TextView} to test the text size without actually showing anything to the user
     */
    private TextView mTestView;

    /**
     * A dummy {@link Paint} to test the text size without actually showing anything to the user
     */
    private Paint mTestPaint;

    /**
     * Scaling factor for fonts. It's a method of calculating independently (!) from the actual density of the screen that is used so users have the
     * same experience on different devices. We will use DisplayMetrics in the Constructor to get the value of the factor and then calculate SP from
     * pixel values
     */
    private float mScaledDensityFactor;

    /**
     * Defines how close we want to be to the factual size of the Text-field. Lower values mean higher precision but also exponentially higher
     * computing cost (more loop runs)
     */
    private final float mThreshold = 0.5f;

    /**
     * Constructor for call without attributes --> invoke constructor with AttributeSet null
     * 
     * @param context
     */
    public AutoFitText(Context context) {
        this(context, null);
    }

    public AutoFitText(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public AutoFitText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        //TextViewPlus part https://stackoverflow.com/a/7197867/2075875
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AutoFitText);
        String customFont = a.getString(R.styleable.AutoFitText_customFont);
        setCustomFont(context, customFont);
        a.recycle();

        // AutoFitText part
        mScaledDensityFactor = context.getResources().getDisplayMetrics().scaledDensity;
        mTestView = new TextView(context);

        mTestPaint = new Paint();
        mTestPaint.set(this.getPaint());

        this.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

            @Override
            public void onGlobalLayout() {
                // make an initial call to onSizeChanged to make sure that refitText is triggered
                onSizeChanged(AutoFitText.this.getWidth(), AutoFitText.this.getHeight(), 0, 0);
                // Remove the LayoutListener immediately so we don't run into an infinite loop
                //AutoFitText.this.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                removeOnGlobalLayoutListener(AutoFitText.this, this);
            }
        });
    }

    public boolean setCustomFont(Context ctx, String asset) {
        Typeface tf = null;
        try {
        tf = Typeface.createFromAsset(ctx.getAssets(), asset);  
        } catch (Exception e) {
            LOG.e(TAG, "Could not get typeface: "+e.getMessage());
            return false;
        }

        setTypeface(tf);  
        return true;
    }

    @SuppressLint("NewApi")
    public static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener){
        if (Build.VERSION.SDK_INT < 16) {
            v.getViewTreeObserver().removeGlobalOnLayoutListener(listener);
        } else {
            v.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
        }
    }

    /**
     * Main method of this widget. Resizes the font so the specified text fits in the text box assuming the text box has the specified width. This is
     * done via a dummy text view that is refit until it matches the real target width and height up to a certain threshold factor
     * 
     * @param targetFieldWidth The width that the TextView currently has and wants filled
     * @param targetFieldHeight The width that the TextView currently has and wants filled
     */
    private void refitText(String text, int targetFieldWidth, int targetFieldHeight) {

        // Variables need to be visible outside the loops for later use. Remember size is in pixels
        float lowerTextSize = MIN_TEXT_SIZE;
        float upperTextSize = MAX_TEXT_SIZE;

        // Force the text to wrap. In principle this is not necessary since the dummy TextView
        // already does this for us but in rare cases adding this line can prevent flickering
        this.setMaxWidth(targetFieldWidth);

        // Padding should not be an issue since we never define it programmatically in this app
        // but just to to be sure we cut it off here
        targetFieldWidth = targetFieldWidth - this.getPaddingLeft() - this.getPaddingRight();
        targetFieldHeight = targetFieldHeight - this.getPaddingTop() - this.getPaddingBottom();

        // Initialize the dummy with some params (that are largely ignored anyway, but this is
        // mandatory to not get a NullPointerException)
        mTestView.setLayoutParams(new LayoutParams(targetFieldWidth, targetFieldHeight));

        // maxWidth is crucial! Otherwise the text would never line wrap but blow up the width
        mTestView.setMaxWidth(targetFieldWidth);

        if (mSingleLine) {
            // the user requested a single line. This is very easy to do since we primarily need to
            // respect the width, don't have to break, don't have to measure...

            /*************************** Converging algorithm 1 ***********************************/
            for (float testSize; (upperTextSize - lowerTextSize) > mThreshold;) {

                // Go to the mean value...
                testSize = (upperTextSize + lowerTextSize) / 2;

                mTestView.setTextSize(TypedValue.COMPLEX_UNIT_SP, testSize / mScaledDensityFactor);
                mTestView.setText(text);
                mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

                if (mTestView.getMeasuredWidth() >= targetFieldWidth) {
                    upperTextSize = testSize; // Font is too big, decrease upperSize
                } else {
                    lowerTextSize = testSize; // Font is too small, increase lowerSize
                }
            }
            /**************************************************************************************/

            // In rare cases with very little letters and width > height we have vertical overlap!
            mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

            if (mTestView.getMeasuredHeight() > targetFieldHeight) {
                upperTextSize = lowerTextSize;
                lowerTextSize = MIN_TEXT_SIZE;

                /*************************** Converging algorithm 1.5 *****************************/
                for (float testSize; (upperTextSize - lowerTextSize) > mThreshold;) {

                    // Go to the mean value...
                    testSize = (upperTextSize + lowerTextSize) / 2;

                    mTestView.setTextSize(TypedValue.COMPLEX_UNIT_SP, testSize / mScaledDensityFactor);
                    mTestView.setText(text);
                    mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

                    if (mTestView.getMeasuredHeight() >= targetFieldHeight) {
                        upperTextSize = testSize; // Font is too big, decrease upperSize
                    } else {
                        lowerTextSize = testSize; // Font is too small, increase lowerSize
                    }
                }
                /**********************************************************************************/
            }
        } else {

            /*********************** Converging algorithm 2 ***************************************/
            // Upper and lower size converge over time. As soon as they're close enough the loop
            // stops
            // TODO probe the algorithm for cost (ATM possibly O(n^2)) and optimize if possible
            for (float testSize; (upperTextSize - lowerTextSize) > mThreshold;) {

                // Go to the mean value...
                testSize = (upperTextSize + lowerTextSize) / 2;

                // ... inflate the dummy TextView by setting a scaled textSize and the text...
                mTestView.setTextSize(TypedValue.COMPLEX_UNIT_SP, testSize / mScaledDensityFactor);
                mTestView.setText(text);

                // ... call measure to find the current values that the text WANTS to occupy
                mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
                int tempHeight = mTestView.getMeasuredHeight();
                // int tempWidth = mTestView.getMeasuredWidth();

                // LOG.debug("Measured: " + tempWidth + "x" + tempHeight);
                // LOG.debug("TextSize: " + testSize / mScaledDensityFactor);

                // ... decide whether those values are appropriate.
                if (tempHeight >= targetFieldHeight) {
                    upperTextSize = testSize; // Font is too big, decrease upperSize
                } else {
                    lowerTextSize = testSize; // Font is too small, increase lowerSize
                }
            }
            /**************************************************************************************/

            // It is possible that a single word is wider than the box. The Android system would
            // wrap this for us. But if you want to decide fo yourself where exactly to break or to
            // add a hyphen or something than you're going to want to implement something like this:
            mTestPaint.setTextSize(lowerTextSize);
            List<String> words = new ArrayList<String>();

            for (String s : text.split(" ")) {
                Log.i("tag", "Word: " + s);
                words.add(s);
            }
            for (String word : words) {
                if (mTestPaint.measureText(word) >= targetFieldWidth) {
                    List<String> pieces = new ArrayList<String>();
                    // pieces = breakWord(word, mTestPaint.measureText(word), targetFieldWidth);

                    // Add code to handle the pieces here...
                }
            }
        }

        /**
         * We are now at most the value of threshold away from the actual size. To rather undershoot than overshoot use the lower value. To match
         * different screens convert to SP first. See {@link http://developer.android.com/guide/topics/resources/more-resources.html#Dimension} for
         * more details
         */
        this.setTextSize(TypedValue.COMPLEX_UNIT_SP, lowerTextSize / mScaledDensityFactor);
        return;
    }

    /**
     * This method receives a call upon a change in text content of the TextView. Unfortunately it is also called - among others - upon text size
     * change which means that we MUST NEVER CALL {@link #refitText(String)} from this method! Doing so would result in an endless loop that would
     * ultimately result in a stack overflow and termination of the application
     * 
     * So for the time being this method does absolutely nothing. If you want to notify the view of a changed text call {@link #setText(CharSequence)}
     */
    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        // Super implementation is also intentionally empty so for now we do absolutely nothing here
        super.onTextChanged(text, start, lengthBefore, lengthAfter);
    }

    @Override
    protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
        if (width != oldWidth && height != oldHeight) {
            refitText(this.getText().toString(), width, height);
        }
    }

    /**
     * This method is guaranteed to be called by {@link TextView#setText(CharSequence)} immediately. Therefore we can safely add our modifications
     * here and then have the parent class resume its work. So if text has changed you should always call {@link TextView#setText(CharSequence)} or
     * {@link TextView#setText(CharSequence, BufferType)} if you know whether the {@link BufferType} is normal, editable or spannable. Note: the
     * method will default to {@link BufferType#NORMAL} if you don't pass an argument.
     */
    @Override
    public void setText(CharSequence text, BufferType type) {

        int targetFieldWidth = this.getWidth();
        int targetFieldHeight = this.getHeight();

        if (targetFieldWidth <= 0 || targetFieldHeight <= 0 || text.equals("")) {
            // Log.v("tag", "Some values are empty, AutoFitText was not able to construct properly");
        } else {
            refitText(text.toString(), targetFieldWidth, targetFieldHeight);
        }
        super.setText(text, type);
    }

    /**
     * TODO add sensibility for {@link #setMaxLines(int)} invocations
     */
    @Override
    public void setMaxLines(int maxLines) {
        // TODO Implement support for this. This could be relatively easy. The idea would probably
        // be to manipulate the targetHeight in the refitText-method and then have the algorithm do
        // its job business as usual. Nonetheless, remember the height will have to be lowered
        // dynamically as the font size shrinks so it won't be a walk in the park still
        if (maxLines == 1) {
            this.setSingleLine(true);
        } else {
            throw new UnsupportedOperationException("MaxLines != 1 are not implemented in AutoFitText yet, use TextView instead");
        }
    }

    @Override
    public void setSingleLine(boolean singleLine) {
        // save the requested value in an instance variable to be able to decide later
        mSingleLine = singleLine;
        super.setSingleLine(singleLine);
    }
}

bilinen hatalar: Android 4.03 ile çalışmaz - yazı tipleri görünmez veya çok küçük (orijinal çığ da çalışmıyor) aşağıdaki hata için geçici bir çözümdür: https://stackoverflow.com/a/21851239/2075875


0

Bunu dene

TextWatcher changeText = new TextWatcher() {
     @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                tv3.setText(et.getText().toString());
                tv3.post(new Runnable() {           
                    @Override
                    public void run() {
                    while(tv3.getLineCount() >= 3){                     
                            tv3.setTextSize((tv3.getTextSize())-1);                     
                        }
                    }
                });
            }

            @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

            @Override public void afterTextChanged(Editable s) { }
        };

Metin boyutunu her değiştirdiğinizde, sadece blok ile bitirdikten sonra (neden olaylar kuyruğuna eklenecek anlamına gelir) çizim neden olacağını sanmıyorum, sanmıyorum. Bu, döngünün metin her değiştiğinde en fazla bir kez çalışacağı ve satır sayısının 3 metni aşmayacağını garanti etmez.
android geliştirici

0

Daha kolay bir şey arıyorsanız:

 public MyTextView extends TextView{

    public void resize(String text, float textViewWidth, float textViewHeight) {
       Paint p = new Paint();
       Rect bounds = new Rect();
       p.setTextSize(1);
       p.getTextBounds(text, 0, text.length(), bounds);
       float widthDifference = (textViewWidth)/bounds.width();
       float heightDifference = (textViewHeight);
       textSize = Math.min(widthDifference, heightDifference);
       setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
}

Çok kısa görünüyor, ama hazırladığım testi geçebilir mi? Ayrıca, bu işlev çağrıldığında anlamıyorum.
android geliştirici

Hayır, daha bir öneri. Testinizi en kısa zamanda geçebilmesi için geliştireceğim.
Sander

Peki, bu bir öneri ise, lütfen doğrudan Github'a gönderebilir misiniz?
android geliştirici

0

@Malachiasz tarafından açıklanan sorun için hızlı düzeltme

Otomatik yeniden boyutlandırma sınıfında bunun için özel destek ekleyerek sorunu çözdüm:

public void setTextCompat(final CharSequence text) {
    setTextCompat(text, BufferType.NORMAL);
}

public void setTextCompat(final CharSequence text, BufferType type) {
    // Quick fix for Android Honeycomb and Ice Cream Sandwich which sets the text only on the first call
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 &&
        Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
        super.setText(DOUBLE_BYTE_WORDJOINER + text + DOUBLE_BYTE_WORDJOINER, type);
    } else {
        super.setText(text, type);
    }
}

@Override
public CharSequence getText() {
    String originalText = super.getText().toString();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 &&
        Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
        // We try to remove the word joiners we added using compat method - if none found - this will do nothing.
        return originalText.replaceAll(DOUBLE_BYTE_WORDJOINER, "");
    } else {
        return originalText;
    }
}

Bunun yourView.setTextCompat(newTextValue)yerine arayourView.setText(newTextValue)


SetText'i geçersiz kılmak yerine neden yeni bir setTextCompat işlevi oluşturmalıyım? Ayrıca, hangi sorun?
android geliştirici

setText (), TextView'in son bir yöntemidir, dolayısıyla onu geçersiz kılamazsınız. Başka bir seçenek bunu özel TextView dışında yapmak olabilir, ancak bu çözüm TextView içinde kullanmak içindir.
Ionut Negru

Tam olarak değil. "SetText" in bazıları özel, bazıları herkese açık, bazıları ise nihai değil. Çoğu, genel void setText (CharSequence metni, BufferType türü) geçersiz kılınarak ele alınabilir. Amacını yine de bilmediğim son bir işlev var: kamu nihai void setText (char [] metin, int başlangıç, int len). Belki koda daha derin bir bakalım.
android geliştirici

Tüm setText () varyantları aslında sonunda setText (CharSequence text) yöntemini çağırır. Aynı davranışı sağlamak istiyorsanız, bu yöntemi geçersiz kılmanız gerekir, aksi takdirde kendi özel setText () yönteminizi eklemeniz çok daha iyi olur.
Ionut Negru

Evet, ama belki de çoğu durumda, sorun değil.
android geliştirici

0

Eklemeyi deneyin LayoutParamsve MaxWidthve MaxHeightiçin TextView. Düzeni taşmaya değil ana kaba saygı duymaya zorlayacaktır.

textview.setLayoutParams(new LayoutParams(LinearLayout.MATCH_PARENT,LinearLayout.WRAP_CONTENT));

int GeneralApproxWidthOfContainer = 400;
int GeneralApproxHeightOfContainer = 600;
textview.setMaxWidth(400);
textview.setMaxHeight(600);` 

Neden bahsettiğinizden emin değilim. Bazen textView örneğine eklemenizi önerir misin, böylece bazen gördüğüm küçük bir sorun olmayacak mı? öyleyse, neden github web sitesinde yayınlamadınız?
android geliştirici

0

Android O'dan beri, metni xml biçiminde otomatik olarak yeniden boyutlandırmak mümkündür:

https://developer.android.com/preview/features/autosizing-textview.html

  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:autoSizeTextType="uniform"
    app:autoSizeMinTextSize="12sp"
    app:autoSizeMaxTextSize="100sp"
    app:autoSizeStepGranularity="2sp"
  />

Android O, TextView ürününün özelliklerini ve sınırlarını temel alarak mizanpajını otomatik olarak doldurması için metin boyutunun genişletilmesine veya daralmasına izin vermesi için bir TextView komutunu vermenizi sağlar. Bu ayar, dinamik içerikli farklı ekranlardaki metin boyutunu optimize etmeyi kolaylaştırır.

Destek Kitaplığı 26.0 Beta sürümü, Android O'dan önceki Android sürümlerini çalıştıran cihazlarda otomatik boyutlandırma TextView özelliğine tam destek sağlar. Kitaplık, Android 4.0 (API düzey 14) ve sonraki sürümlerini destekler. Android.support.v4.widget paketi, özelliklere geriye dönük uyumlu bir şekilde erişmek için TextViewCompat sınıfını içerir.


Ne yazık ki, testlerime göre ve hatta Google IO'nun videosunda, yazı tipini yeniden boyutlandırmak yerine yanlış kelimeleri kısmi sarma gibi sorunları olduğunu fark ettim. Bunu burada bildirdim: issuetracker.google.com/issues/38468964 ve bu yüzden hala kullanmıyorum.
android geliştirici

0

Android resmi Otomatik Boyutlandırma TextView'u denedikten sonra , Android sürümünüzün Android 8.0'dan (API düzey 26) önce olup olmadığını, kullanmanız android.support.v7.widget.AppCompatTextViewve destek kitaplığı sürümünüzün 26.0.0'ın üzerinde olduğundan emin oldum. Misal:

<android.support.v7.widget.AppCompatTextView
    android:layout_width="130dp"
    android:layout_height="32dp"
    android:maxLines="1"
    app:autoSizeMaxTextSize="22sp"
    app:autoSizeMinTextSize="12sp"
    app:autoSizeStepGranularity="2sp"
    app:autoSizeTextType="uniform" />

güncelleme :

@ Android-geliştiricinin cevabına göre, AppCompatActivitykaynak kodunu kontrol ediyorum ve bu iki satırıonCreate

final AppCompatDelegate delegate = getDelegate(); delegate.installViewFactory();

ve AppCompatDelegateImpl'lercreateView

    if (mAppCompatViewInflater == null) {
        mAppCompatViewInflater = new AppCompatViewInflater();
    }

inflatter AppCompatViewInflatergörünümü kullanır , AppCompatViewInflatercreateView, "TextView" için AppCompatTextView kullanır.

public final View createView(){
    ...
    View view = null;
    switch (name) {
        case "TextView":
            view = new AppCompatTextView(context, attrs);
            break;
        case "ImageView":
            view = new AppCompatImageView(context, attrs);
            break;
        case "Button":
            view = new AppCompatButton(context, attrs);
            break;
    ...
}

Projemde kullanmıyorum AppCompatActivity, bu yüzden xml'de kullanmam gerekiyor <android.support.v7.widget.AppCompatTextView>.


1
Çoğu durumda, inflatter yalnızca "TextView" yazdığınızda AppCompatTextView kullanır, bu yüzden bunu yapmanız gerekmez.
android geliştirici

@androiddeveloper Ama TextView kullandığımda, otomatik boyutlandırma çalışmıyor.
weei.zh

@androiddeveloper Teşekkürler, nedeni kullanmıyorum AppCompatActivity. AppCompatActivity, AppCompatViewInflater inflater düzenini kullanır.
weei.zh

Anlamıyorum
android geliştirici
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.