Pasangkan teks secara otomatis untuk Android

231

Latar Belakang

Sering kali kita perlu menyesuaikan font TextView secara otomatis dengan batasan yang diberikan padanya.

Masalah

Sedihnya, meskipun ada banyak utas dan pos (dan solusi yang disarankan) membicarakan masalah ini (contoh di sini , di sini dan di sini ), tidak satu pun dari mereka yang benar-benar berfungsi dengan baik.

Itu sebabnya, saya memutuskan untuk menguji masing-masing sampai saya menemukan real deal.

Saya pikir persyaratan dari textView tersebut harus:

  1. Harus memungkinkan penggunaan font, jenis huruf, gaya, dan serangkaian karakter apa pun.

  2. Harus menangani lebar dan tinggi

  3. Tidak ada pemotongan kecuali teks tidak dapat ditampung karena batasan, kami berikan padanya (contoh: teks terlalu panjang, ukuran terlalu kecil yang tersedia). Namun, kami dapat meminta bilah gulir horizontal / vertikal jika diinginkan, hanya untuk kasus-kasus tersebut.

  4. Harus memungkinkan multi-line atau single-line. Dalam hal multi-line, izinkan jalur maks & min.

  5. Seharusnya tidak lambat dalam perhitungan. Menggunakan loop untuk menemukan ukuran terbaik? Setidaknya optimalkan dan jangan tambahkan sampel Anda sebanyak 1 kali setiap kali.

  6. Dalam kasus multi-line, harus memungkinkan untuk lebih suka mengubah ukuran atau menggunakan lebih banyak baris, dan / atau memungkinkan untuk memilih sendiri garis dengan menggunakan karakter "\ n".

Apa yang saya coba

Saya sudah mencoba begitu banyak sampel (termasuk yang ada di tautan, saya sudah menulis), dan saya juga mencoba memodifikasinya untuk menangani kasus-kasus, saya sudah bicarakan, tetapi tidak ada yang benar-benar berfungsi.

Saya telah membuat proyek sampel yang memungkinkan saya untuk melihat apakah TextView secara otomatis cocok dengan benar.

Saat ini, proyek sampel saya hanya mengacak teks (alfabet Inggris plus digit) dan ukuran textView, dan membiarkannya tetap dengan satu baris, tetapi bahkan ini tidak bekerja dengan baik pada sampel yang saya coba.

Berikut kodenya (juga tersedia di sini ):

Mengajukan 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>

Mengajukan 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();
    }
  }

Pertanyaan

Apakah ada yang tahu solusi untuk masalah umum ini yang benar-benar berfungsi?

Bahkan solusi yang memiliki fitur jauh lebih sedikit dari apa yang saya tulis, misalnya yang hanya memiliki jumlah baris teks yang konstan, dan menyesuaikan fontnya sesuai dengan ukurannya, namun tidak pernah memiliki gangguan aneh dan membuat teks terlalu besar / kecil dibandingkan dengan ruang yang tersedia.


Proyek GitHub

Karena ini adalah TextView yang sangat penting, saya memutuskan untuk menerbitkan perpustakaan, sehingga semua orang dapat dengan mudah menggunakannya, dan berkontribusi di sini .

pengembang android
sumber
Sudahkah Anda mencoba yang ini? androidviews.net/2012/12/autoscale-textview
Thrakbad
@Thrakbad itu salah satu tautan yang saya sebutkan. Itu juga tidak lulus ujian.
pengembang android
Ah maaf, entah bagaimana saya melewatkan contoh terakhir
Thrakbad
Ya, tolong percayalah, saya sudah mencoba banyak sampel dan saya juga mencoba memodifikasinya untuk memperbaiki masalah yang saya temukan, tetapi tidak pernah berhasil. Jika Anda menemukan sesuatu yang menurut Anda mungkin berhasil, silakan coba. Saya telah memposting kode sampel hanya untuk ini.
pengembang android
1
@ aturan ini adalah salah satu posting yang sudah saya baca, dan saya sudah menguji semua sampel kode. juga saya pikir Anda sudah memposting ganda.
pengembang android

Jawaban:

147

Berkat MartinH ini memperbaiki sederhana di sini , kode ini juga mengurus android:drawableLeft, android:drawableRight, android:drawableTopdan android:drawableBottomtag.


Jawaban saya di sini seharusnya membuat Anda senang Text Scale TextView Text agar Sesuai dalam Batas

Saya telah memodifikasi test case Anda:

@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);
        }
    });
}

Saya memposting kode di sini sesuai permintaan pengembang android :

Efek akhir:

Masukkan deskripsi gambar di sini

File Tata Letak Sampel:

<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>

Dan kode Java:

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();
        }
    }
}

Peringatan:

Waspadalah terhadap bug yang diselesaikan ini di Android 3.1 (Honeycomb).

M-WaJeEh
sumber
ok, saya sudah menguji kodenya, dan sepertinya baik-baik saja. namun, Anda menggunakan getMaxLines (), yang untuk API 16, tetapi Anda dapat menyimpan maxLines dan mendapatkannya dengan menimpa setMaxLines dan menyimpan nilainya. Saya telah mengubahnya dan sekarang berfungsi dengan baik. juga, karena kadang-kadang teks mungkin terlalu panjang untuk ukuran terkecil, saya sudah mencoba menggunakan setEllipsize, dan itu berhasil juga! Saya pikir kami memiliki pemenang. tolong kirim kode Anda di sini sehingga saya dapat menandainya sebagai yang benar.
pengembang android
Anda tidak menghapus getMaxLines () dan menggunakan milik Anda sendiri. masih akan berfungsi hanya dari API16 ... tolong ubah sehingga semua orang bisa menggunakannya. saya sarankan untuk mengesampingkan setMaxLines untuk menyimpan parameter ke bidang dan kemudian mengakses bidang alih-alih menggunakan getMaxLines.
pengembang android
periksa sekarang, tambahkan dukungan untuk saluran maks.
M-WaJeEh
sepertinya baik-baik saja. Anda harus menambahkan "@TargetApi (Build.VERSION_CODES.JELLY_BEAN)" ke metode onTestSize () sehingga Lint tidak akan memberikan kesalahan / peringatan tentang itu, dan menginisialisasinya dalam CTOR sebagai gantinya. mengatur pencarian biner ke statis juga merupakan hal yang baik, dan Anda bisa melakukan inisialisasi pada CTOR terbesar dan menyebutnya dari CTOR lain. Namun, itu benar-benar jawaban terbaik dan Anda layak mendapat nilai V. ini akhirnya jawaban yang bagus untuk pertanyaan lama ini. kerja bagus!
pengembang android
@ M-WaJeEh periksa pos baru di utas ini. seorang pengguna bernama "MartinH" mengatakan bahwa ia memiliki perbaikan untuk kode Anda.
pengembang android
14

Saya telah memodifikasi jawaban M-WaJeEh sedikit untuk memperhitungkan drawable akun.

The getCompoundPaddingXXXX()metode kembali padding of the view + drawable space. Lihat misalnya: TextView.getCompoundPaddingLeft ()

Masalah: Ini memperbaiki pengukuran lebar dan tinggi ruang TextView yang tersedia untuk teks. Jika kami tidak memperhitungkan ukuran yang dapat ditarik, itu diabaikan dan teks akan berakhir tumpang tindih dengan yang dapat ditarik.


Segmen yang diperbarui 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));
}
MartinH
sumber
Anda telah memperbarui sumber M-WaJeEh. mengapa Anda memasukkannya sebagai jawaban baru? saya tahu Anda memiliki niat baik, tetapi apakah itu hal yang baik untuk meletakkannya sebagai jawaban? Saya tidak yakin dia dapat melihat pemberitahuan jawaban Anda ...
Pengembang Android
1
Saya baru saja mendaftar untuk dapat memberikan pembaruan itu. Jumlah reputasi saya hanya 1 yang mencegah saya mengomentari posting yang bukan milik saya (minimal 15).
MartinH
Jangan khawatir, mungkin Anda ingin mengirimkan jawaban saya kepada M-WajeEh karena saya bahkan tidak dapat menghubunginya saat ini: /
MartinH
Bisakah Anda mencoba menggambarkan dengan lebih baik apa yang telah Anda lakukan? apakah Anda memperbaiki bug? dapatkah Anda menampilkan tangkapan layar untuk menunjukkan bug?
pengembang android
1
Hai @MartinH, Selamat Datang di SO. Saya akan bermuka masam melihat ini dan akan memperbarui jawaban saya dengan menyebutkan nama Anda dan memposting tautan ke posting Anda setelah pengujian . Tolong beri saya beberapa hari saja. Saya terjebak di tempat lain saat ini.
M-WaJeEh
7

Ok Saya telah menggunakan pekan lalu secara besar-besaran menulis ulang kode saya untuk tepat sesuai dengan tes Anda. Anda sekarang dapat menyalin 1: 1 ini dan itu akan segera berfungsi - termasuk setSingleLine(). Harap diingat untuk menyesuaikan MIN_TEXT_SIZEdanMAX_TEXT_SIZE jika Anda menggunakan nilai ekstrem.

Algoritma konvergen terlihat seperti ini:

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
    }
}

Dan seluruh kelas dapat ditemukan di sini.

Hasilnya sangat fleksibel sekarang. Ini berfungsi sama dinyatakan dalam xml seperti:

<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" />

... serta dibangun secara terprogram seperti dalam pengujian Anda.

Saya sangat berharap Anda dapat menggunakan ini sekarang. Anda dapat menelepon setText(CharSequence text)sekarang untuk menggunakannya. Kelas menangani pengecualian luar biasa langka dan harus solid. Satu-satunya hal yang belum didukung oleh algoritma adalah:

  • Panggilan ke setMaxLines(x)tempatx >= 2

Tetapi saya telah menambahkan komentar luas untuk membantu Anda membangun ini jika Anda mau!


Tolong dicatat:

Jika Anda hanya menggunakan ini secara normal tanpa membatasinya menjadi satu baris saja maka mungkin ada pemecahan kata seperti yang Anda sebutkan sebelumnya. Ini adalah fitur Android , bukan kesalahan AutoFitText. Android akan selalu memecahkan kata-kata yang terlalu panjang untuk TextView dan itu sebenarnya cukup nyaman. Jika Anda ingin campur tangan di sini daripada silakan lihat komentar dan kode saya mulai dari baris 203. Saya sudah menulis pemisahan yang memadai dan pengakuan untuk Anda, yang perlu Anda lakukan selanjutnya adalah membagi kata-kata dan kemudian memodifikasi sesuai keinginan .

Kesimpulannya: Anda harus mempertimbangkan untuk menulis ulang tes Anda untuk mendukung ruang angkasa, seperti:

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());

Ini akan menghasilkan hasil yang sangat indah (dan lebih realistis).
Anda akan menemukan komentar untuk memulai Anda dalam masalah ini juga.

Semoga sukses dan salam

Patrick
sumber
ada beberapa masalah, tetapi secara keseluruhan itu bekerja dengan sangat baik. masalah: tidak mendukung multi baris (seperti yang Anda tulis) dan akan merusak kata jika Anda mencoba melakukannya, termasuk fungsi API16 (addOnGlobalLayoutListener) yang menurut saya dapat sepenuhnya dihindari (atau setidaknya menggunakan OnPreDrawListener sebagai gantinya), menggunakan pencarian biner sehingga dapat menguji ukuran yang tidak cocok sama sekali (dan menggunakan lebih banyak memori tanpa alasan, yang dapat menyebabkan pengecualian jika terlalu besar), tidak mendukung penanganan ketika ada perubahan (seperti teks itu sendiri).
pengembang android
btw, Anda tidak harus menggunakan variabel mTestView dan melakukan pengujian langsung pada tampilan saat ini, dan dengan cara ini Anda tidak perlu mempertimbangkan semua hal khusus. juga, saya berpikir bahwa alih-alih MAX_TEXT_SIZE Anda dapat menggunakan targetFieldHeight.
pengembang android
saya berpikir bahwa alih-alih pencarian biner murni (terutama 2 dari mereka), Anda bisa mendapatkan tebakan yang lebih baik dengan rasio target terhadap ukuran yang diukur. ada juga metode lain (newton dan sesuatu yang saya tidak ingat) tetapi saya tidak berpikir mereka cocok di sini yang saya sarankan sekarang lebih baik karena Anda bisa mulai dari ukuran min daripada menguji ukuran max. faktanya adalah Anda tidak akan membutuhkan ukuran max karena Anda terus membuat tebakan yang lebih baik.
pengembang android
saya juga berpikir bahwa 2 pencarian biner dapat digabung menjadi satu baris ketika kondisinya bisa saja: if (getMeasuredWidth ()> = targetFieldWidth || getMeasuredHeight ()> = targetFieldHeight)
pengembang android
1
masalah lain yang saya temukan adalah gravitasi teks. saya pikir itu tidak dapat dipusatkan secara vertikal.
pengembang android
4

Persyaratan saya adalah untuk

  • Klik pada ScalableTextView
  • Buka listActivity dan tampilkan berbagai item string panjang.
  • Pilih teks dari daftar.
  • Atur teks kembali ke ScalableTextView di aktivitas lain.

Saya merujuk tautan: Skala Otomatis TextView Teks agar Sesuai dengan Batas (termasuk komentar) dan juga DialogTitle.java

Saya menemukan bahwa solusi yang diberikan bagus dan sederhana tetapi tidak secara dinamis mengubah ukuran kotak teks. Ini bekerja dengan baik ketika panjang teks yang dipilih dari tampilan daftar lebih besar dalam ukuran daripada panjang teks yang ada di ScalableTextView . Ketika memilih teks yang memiliki panjang lebih kecil dari teks yang ada di ScalableTextView, itu tidak menambah ukuran teks, menunjukkan teks dalam ukuran lebih kecil.

Saya memodifikasi ScalableTextView.java untuk menyesuaikan kembali ukuran teks berdasarkan panjang teks. Ini milik sayaScalableTextView.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);
            }
        }
    }
}
}

Selamat Coding ....

Devendra Vaja
sumber
Tidakkah seharusnya Anda melakukannya menggunakan pencarian biner alih-alih mengurangi satu untuk setiap iterasi? Selain itu, tampaknya kode ini memaksa TextView dengan satu baris teks, alih-alih membiarkannya memiliki beberapa baris.
Pengembang Android
Ini adalah satu-satunya solusi yang berhasil untuk saya. Masalah saya terkait dengan TExtView yang tidak sesuai dengan tampilan hanya dalam 4.1
FOliveira
sepertinya tidak cocok dengan orang tua untuk 100%. lihat tangkapan layar: s14.postimg.org/93c2xgs75/Screenshot_2.png
user25
4

Saya akan menjelaskan cara kerja atribut ini versi Android yang lebih rendah selangkah demi selangkah:

1- Impor pustaka dukungan android 26.xx pada file gradle proyek Anda. Jika tidak ada perpustakaan dukungan di IDE, mereka akan mengunduh secara otomatis.

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- Buka file XML tata letak Anda dan refactor seperti ini tag TextView Anda. Skenario ini adalah: ketika ukuran font bertambah pada sistem, sesuaikan teks dengan lebar yang tersedia, bukan bungkus kata.

<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" />
Sinan Ergin
sumber
Implementasi mereka dari TextView auto-fit tidak bekerja dengan baik. Ada kalanya alih-alih mengubah ukuran ukuran font, teks hanya membungkus ke baris berikutnya, buruk ... Itu bahkan ditampilkan di video mereka, seolah-olah itu hal yang baik: youtu.be/fjUdJ2aVqE4?t=14 . Perhatikan bagaimana "w" dari "TextView" menuju ke baris berikutnya ... Pokoknya, buat permintaan baru di sini: issuetracker.google.com/issues/68787108 . Silakan pertimbangkan untuk membintangi itu.
pengembang android
@androiddeveloper Anda benar. Saya tidak menjelaskan implementasi tag ini bekerja satu baris. Jika Anda perlu membungkus baris, Anda harus mengubah atribut ini: android: layout_height = "wrap_content". Tetapi android.support.v7.widget.AppCompatTextView dan jaminan implementasi gradle berfungsi sebagai atribut ini.
Sinan Ergin
Saya pikir Anda tidak mengerti. Saya tidak ingin bagian-kata untuk dibungkus ke baris berikutnya, kecuali tidak ada cara lain untuk menyelesaikannya. Dalam video, Anda dapat dengan mudah melihat font yang bisa semakin kecil saja. Itu masalahnya. Apakah Anda mengatakan Anda tahu cara memperbaikinya? Berarti tidak akan ada pembungkus kata, dan hanya ukuran font yang berubah?
pengembang android
@androiddeveloper Ada banyak skenario. Skenario saya adalah: teks autofit untuk diperbaiki dengan dan bukan bungkus kata. Selama penambahan ukuran font sistem, teks harus menunjukkan bukan bungkus kata dalam tampilan teks. Jika bungkus kata tidak penting bagi Anda, jangan repot-repot mengatur ketinggian tetap. Properti ini tidak hanya menggunakan bungkus kata, Anda dapat autofit teks telah memperbaiki lebar TextView.
Sinan Ergin
Menurut pengujian saya, ukuran font tidak berubah dengan benar, dan masalah bungkus kata dapat terjadi.
pengembang android
3

Peringatan, bug di Android 3 (Honeycomb) dan Android 4.0 (Ice Cream Sandwich)

Versi Android: 3.1 - 4.04 memiliki bug, yang setTextSize () di dalam TextView hanya berfungsi untuk pertama kalinya (doa pertama).

Bug ini dijelaskan dalam Edisi 22493: Bug ketinggian TextView di Android 4.0 dan Edisi 17343: tinggi dan teks tombol tidak kembali ke kondisi semula setelah menambah dan mengurangi ukuran teks pada HoneyComb .

Solusinya adalah menambahkan karakter baris baru ke teks yang ditetapkan ke TextView sebelum mengubah ukuran:

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

Saya menggunakannya dalam kode saya sebagai berikut:

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);

Saya menambahkan karakter "\ u3000" ini di kiri dan kanan teks saya, agar tetap terpusat. Jika Anda telah menyelaraskan ke kiri lalu tambahkan ke kanan saja. Tentu saja dapat juga tertanam dengan widget AutoResizeTextView, tetapi saya ingin tetap memperbaiki kode di luar.

Malachiasz
sumber
dapatkah Anda menunjukkan dengan tepat pada baris mana (sebelum / sesudah baris mana) dan pada fungsi yang mana?
pengembang android
Ok terima kasih. Saya harap ini memperbaiki segalanya, dan siapa pun yang membaca ini akan merasa terbantu. Saat ini saya tidak bisa memeriksanya. mungkin lain kali saya akan menggunakannya.
pengembang android
Kode ini dipanggil dari luar kode view. Apakah Anda tahu mungkin cara alternatif yang baik untuk menangani ini di dalam kode tampilan?
pengembang android
Anda bisa mengganti textView.setText () dan meletakkan kode ini di dalam kelas TextView
Malachiasz
Bukankah itu juga berarti bahwa "getText ()" akan mengembalikan teks dengan karakter tambahan?
pengembang android
3

Sekarang ada solusi resmi untuk masalah ini. Autosizing TextViews yang diperkenalkan dengan Android O tersedia di Perpustakaan Dukungan 26 dan kompatibel sepenuhnya hingga Android 4.0.

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

Saya tidak yakin mengapa https://stackoverflow.com/a/42940171/47680 yang juga termasuk informasi ini dihapus oleh admin.

Artem Russakovskii
sumber
1
Ya, saya juga pernah mendengarnya, tapi seberapa bagus itu? Apakah ada sampel menggunakannya? Bahkan dalam video mereka, saya telah melihat bug: surat dari satu kata sampai ke baris setelah (dibungkus): youtu.be/1N9KveJ-FU8?t=781 (perhatikan "W")
pengembang android
3

Dari Juni 2018 Android secara resmi mendukung fitur ini untuk Android 4.0 (API level 14) dan lebih tinggi.
Dengan Android 8.0 (API level 26) dan lebih tinggi:

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

Versi Android sebelum Android 8.0 (API level 26):

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

Lihat jawaban detail saya .

Pikirkan Kode Dua Kali Sekali
sumber
1

Konversi tampilan teks menjadi gambar, dan skala gambar dalam batas-batas.

Berikut ini contoh cara mengonversi tampilan ke Gambar: Mengonversi tampilan ke Bitmap tanpa menampilkannya di Android?

Masalahnya adalah, teks Anda tidak akan dapat dipilih, tetapi harus melakukan trik. Saya belum mencobanya, jadi saya tidak yakin bagaimana tampilannya (karena penskalaan).

Dr NotSoKind
sumber
Itu ide yang bagus, tetapi ini juga akan membuat teks pixelated dan menghapus semua fitur yang dapat saya gunakan dengan textView.
pengembang android
0

Di bawah ini adalah avalancha TextView dengan fungsionalitas tambahan untuk Font kustom.

Pemakaian:

<?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>

Jangan lupa untuk menambahkan: xmlns: foo = "http://schemas.android.com/apk/res-auto". Font harus dalam firectory aset

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);
    }
}

bug yang diketahui: Tidak bekerja dengan Android 4.03 - fonta tidak terlihat atau sangat kecil (avalancha asli tidak berfungsi juga) di bawah ini adalah solusi untuk bug itu: https://stackoverflow.com/a/21851239/2075875

Malachiasz
sumber
0

Coba ini

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) { }
        };
Lupa Nama
sumber
Saya tidak berpikir ini akan berhasil, karena setiap kali Anda mengubah ukuran teks, itu akan menyebabkan gambar hanya setelah Anda berakhir dengan blok (artinya akan ditambahkan ke antrian acara). Ini berarti bahwa loop hanya akan bekerja paling banyak sekali setiap kali teks berubah, dan itu tidak menjamin bahwa jumlah baris tidak akan melebihi 3 baris teks.
pengembang android
0

Jika Anda mencari sesuatu yang lebih mudah:

 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);
}
Sander
sumber
Tampaknya memang sangat singkat, tetapi bisakah ia lulus ujian yang telah saya siapkan? Juga, saya tidak mengerti ketika fungsi ini dipanggil.
pengembang android
Tidak, itu lebih merupakan saran. Saya akan memperbaikinya sehingga dapat lulus tes Anda secepatnya.
Sander
Nah, jika itu saran, bisakah Anda mempostingnya langsung ke Github?
pengembang android
0

Perbaikan cepat untuk masalah yang dijelaskan oleh @Malachiasz

Saya telah memperbaiki masalah dengan menambahkan dukungan khusus untuk ini di kelas ubah ukuran otomatis:

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;
    }
}

Panggil saja yourView.setTextCompat(newTextValue)alih-alihyourView.setText(newTextValue)

Ionut Negru
sumber
Mengapa membuat fungsi baru setTextCompat, alih-alih mengganti setText? Juga, masalah apa?
pengembang android
setText () adalah metode terakhir dari TextView, sehingga Anda tidak akan bisa menimpanya. Opsi lain adalah melakukan ini di luar TextView kustom, tetapi solusi ini adalah untuk menggunakannya di dalam TextView.
Ionut Negru
Tidak persis. Beberapa "setText" bersifat pribadi, beberapa bersifat publik, dan sebagian publik tidak bersifat final. Tampaknya sebagian besar dapat ditangani dengan mengganti setText public void (teks CharSequence, tipe BufferType). Ada fungsi terakhir yang saya tidak tahu tujuannya: public final void setText (char [] text, int start, int len). Mungkin lebih dalam melihat kode.
Android developer
Semua varian setText () akan benar-benar memanggil metode setText (CharSequence text) pada akhirnya. Jika Anda ingin memastikan perilaku yang sama, Anda harus mengganti metode itu, jika tidak, akan lebih baik dengan menambahkan metode setText () kustom Anda sendiri.
Ionut Negru
Ya, tapi mungkin untuk sebagian besar kasus, tidak masalah.
pengembang android
0

Coba tambahkan LayoutParamsdan MaxWidthdan MaxHeightke TextView. Ini akan memaksa tata letak untuk menghormati wadah induk dan tidak meluap.

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

int GeneralApproxWidthOfContainer = 400;
int GeneralApproxHeightOfContainer = 600;
textview.setMaxWidth(400);
textview.setMaxHeight(600);` 
pengguna3891647
sumber
Saya tidak yakin apa yang sedang Anda bicarakan. Apakah Anda menyarankan menambahkan ini ke sampel textView, sehingga tidak akan memiliki masalah kecil yang kadang-kadang saya lihat? jika demikian, mengapa Anda tidak mempostingnya di situs web github?
pengembang android
0

Karena Android O, dimungkinkan untuk mengubah ukuran teks secara otomatis dalam xml:

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 memungkinkan Anda menginstruksikan TextView untuk membiarkan ukuran teks membesar atau berkontraksi secara otomatis untuk mengisi tata letaknya berdasarkan karakteristik dan batasan TextView. Pengaturan ini memudahkan untuk mengoptimalkan ukuran teks pada layar yang berbeda dengan konten dinamis.

Perpustakaan Dukungan 26.0 Beta menyediakan dukungan penuh untuk fitur TextView autosisasi pada perangkat yang menjalankan versi Android sebelum Android O. Perpustakaan menyediakan dukungan untuk Android 4.0 (API level 14) dan lebih tinggi. Paket android.support.v4.widget berisi kelas TextViewCompat untuk mengakses fitur dengan cara yang kompatibel-mundur.

Jutaan
sumber
Sayangnya, menurut pengujian saya, dan bahkan dalam video Google IO, saya perhatikan ada masalah, seperti salah membungkus kata-kata, alih-alih mengubah ukuran font. Saya sudah melaporkan ini di sini: issuetracker.google.com/issues/38468964 , dan inilah mengapa saya masih belum menggunakannya.
pengembang android
0

Setelah saya mencoba Android resmi Autosizing TextView , saya menemukan jika versi Android Anda sebelum Android 8.0 (API level 26), Anda perlu menggunakan android.support.v7.widget.AppCompatTextView, dan pastikan versi perpustakaan dukungan Anda di atas 26.0.0. Contoh:

<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" />

perbarui :

Menurut balasan @ android-developer, saya memeriksa AppCompatActivitykode sumber, dan menemukan dua baris ini dionCreate

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

dan di AppCompatDelegateImpl'screateView

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

itu menggunakan AppCompatViewInflatertampilan inflater, ketika AppCompatViewInflatercreateView itu akan menggunakan AppCompatTextView untuk "TextView".

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;
    ...
}

Dalam proyek saya, saya tidak menggunakan AppCompatActivity, jadi saya perlu menggunakan <android.support.v7.widget.AppCompatTextView>dalam xml.

weei.zh
sumber
1
Dalam kebanyakan kasus, inflater menggunakan AppCompatTextView ketika Anda menulis "TextView" saja, jadi Anda tidak harus melakukannya.
pengembang android
@androiddeveloper Tetapi ketika saya menggunakan TextView, autosizing tidak berfungsi.
weei.zh
@androiddeveloper Terima kasih, alasannya saya tidak menggunakan AppCompatActivity. AppCompatActivity menggunakan tata letak inflater inflater AppCompatViewInflater.
weei.zh
Saya tidak mengerti
pengembang android