package com.stackdump.bitmapautolinefeed; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.util.Log; import android.view.Display; import android.view.View; import android.view.WindowManager; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import com.stackdump.bitmapautolinefeed.utils.SurfaceViewUtils; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { //屏幕的宽度 private int mDisplayX; //屏幕的高度 private int mDisplayY; private EditText mEditText; private ImageView mImage; private Button mBtn; private TextPaint mPaint; //需要生成的图片 private Bitmap mBitmap; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mEditText = (EditText)findViewById(R.id.text); mImage = (ImageView) findViewById(R.id.image); mBtn = (Button) findViewById(R.id.btn); //获取屏幕的宽度和高度,获取的宽度用于计算何时自动换行 WindowManager manager = (WindowManager)getSystemService(Context.WINDOW_SERVICE); Display display = manager.getDefaultDisplay(); Point size = new Point(); display.getSize(size); mDisplayX = size.x; mDisplayY = size.y; mBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //存在内存泄露的风险,需自行优化 new AsyncTask<Void, Void, Boolean>() { @Override protected void onPreExecute() { } @Override protected Boolean doInBackground(Void... unused) { Log.d("info","doInBackground"); return saveBmp(mEditText.getText().toString()); } @Override protected void onProgressUpdate(Void... unused) { } @Override protected void onPostExecute(Boolean result) { Log.d("info","getFilesDir:"+getFilesDir());//存储在内置存储 Log.d("info","getExternalStorageDirectory:"+Environment.getExternalStorageDirectory());//外置存储路径 Bitmap bmp = getLoacalBitmap(getFilesDir()+"/siteName.bmp"); if(bmp != null) { mImage.setImageBitmap(bmp); } } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } }); } private boolean saveBmp(String text) { try { initializePaint(); //文字是单行时的总宽度(先计算单行的长度) int textWidth; //文字是单行时的总高度(先计算单行的高度) int textHeight; textWidth = (int) mPaint.measureText(text); Paint.FontMetrics fm = mPaint.getFontMetrics(); textHeight = (int) Math.ceil(fm.bottom - fm.top); Log.d("info","textWidth:"+textWidth+" textHeight:"+textHeight); Log.d("info","mDisplayX:"+mDisplayX+" mDisplayY:"+mDisplayY); mBitmap = getNewBitMap(text, textWidth, textHeight, Math.abs(fm.top), mPaint); saveFileHighQuality(mBitmap); } catch (StringIndexOutOfBoundsException e) { return false; } catch (Exception e) { return false; } return true; } public Bitmap getNewBitMap(String text, int textWidth, int textHeight, float top, TextPaint textPaint) { //水平偏移量,设置该值,文字可向右偏移一定距离 int horizontalOffset = 60; int verticalOffset = 60; //The text may be more than one line,so calculate the number of rows int rows = textWidth / (mDisplayX-horizontalOffset); rows = (textWidth % (mDisplayX-horizontalOffset) > 0 ) ? rows + 1 : rows; // int bmpWidth = rows > 1 ? mDisplayX : (textWidth+horizontalOffset); int bmpHeight = Math.min(mDisplayY, textHeight*rows+verticalOffset); Bitmap newBitmap = Bitmap.createBitmap(bmpWidth,bmpHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(newBitmap); canvas.drawBitmap(newBitmap, 0, top, textPaint); canvas.translate(horizontalOffset, verticalOffset); /* 参数含义: 1.字符串子资源 2 .画笔对象 3.layout的宽度,字符串超出宽度时自动换行。(屏幕总宽度-水平平移量就是layout宽度) 4.layout的样式,有ALIGN_CENTER, ALIGN_NORMAL, ALIGN_OPPOSITE 三种。 5.相对行间距,相对字体大小,1.5f表示行间距为1.5倍的字体高度。 6.相对行间距,0表示0个像素。 实际行间距等于这两者的和。 7.还不知道是什么意思,参数名是boolean includepad。 需要指出的是这个layout是默认画在Canvas的(0,0)点的,如果需要调整位置只能在draw之前移Canvas的起始坐标 canvas.translate(x,y); */ StaticLayout sl= new StaticLayout(text, textPaint, mDisplayX-horizontalOffset, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); sl.draw(canvas); return newBitmap; } private void initializePaint() { mPaint = new TextPaint(); //抗锯齿,设置为true,边界明显变模糊 mPaint.setAntiAlias(true); //防抖动,设置为true,边界明显变模糊 mPaint.setDither(true); mPaint.setColor(Color.RED); //透明度0-255,数值越小越透明 mPaint.setAlpha(255); mPaint.setTextSize(100); } private boolean saveFileHighQuality(Bitmap bm) { if (!Environment.MEDIA_MOUNTED.equals(Environment .getExternalStorageState())) { return false; } FileOutputStream out = null; try { out = openFileOutput("siteName.bmp", Context.MODE_PRIVATE); int w = bm.getWidth(); int h = bm.getHeight(); int[] pixels = new int[w * h]; bm.getPixels(pixels, 0, w, 0, 0, w, h); byte[] rgb = SurfaceViewUtils.addBMP_RGB_888(pixels, w, h); byte[] header = SurfaceViewUtils.addBMPImageHeader(rgb.length); byte[] infos = SurfaceViewUtils.addBMPImageInfosHeader(w, h); byte[] buffer = new byte[54 + rgb.length]; System.arraycopy(header, 0, buffer, 0, header.length); System.arraycopy(infos, 0, buffer, 14, infos.length); System.arraycopy(rgb, 0, buffer, 54, rgb.length); out.write(buffer); //将输入流和输出流中的缓冲进行刷新,使缓冲区中的元素即时做输入和输出,而不必等缓冲区满 out.flush(); } catch (FileNotFoundException e) { return false; } catch (IOException e) { return false; } catch (Exception e) { return false; } finally { if (out != null) { try { out.close(); } catch (IOException e) { // TODO Auto-generated catch block } } if (!mBitmap.isRecycled()) { mBitmap.recycle(); //作用只是提醒或告诉虚拟机,希望进行一次垃圾回收。 //至于什么时候进行回收还是取决于虚拟机,而且也不能保证一定进行回收(如果-XX:+DisableExplicitGC设置成true,则不会进行回收) System.gc(); } } return true; } /** * 加载本地图片 * 本地图片路径 * @param path * @return */ public static Bitmap getLoacalBitmap(String path) { try { FileInputStream fis = new FileInputStream(path); return BitmapFactory.decodeStream(fis); } catch (FileNotFoundException e) { e.printStackTrace(); return null; } } /** * 从服务器取图片 * @param url * @return */ public static Bitmap getHttpBitmap(String url) { URL myFileUrl = null; Bitmap bitmap = null; try { Log.d("info", url); myFileUrl = new URL(url); } catch (MalformedURLException e) { e.printStackTrace(); } try { HttpURLConnection conn = (HttpURLConnection) myFileUrl.openConnection(); conn.setConnectTimeout(10); conn.setReadTimeout(10); conn.setDoInput(true); conn.connect(); InputStream is = conn.getInputStream(); bitmap = BitmapFactory.decodeStream(is); is.close(); } catch (IOException e) { e.printStackTrace(); } return bitmap; } }
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="click"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/colorAccent"
app:layout_constraintLeft_toRightOf="@+id/btn"/>
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/btn"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
效果:
1.单行且无平移:

2.多行且无平移

3.单行且有平移:

4.多行且有平移:

0