產品今天說項目分享時要分享出一張 封面圖片 + 幾行文字 + 二維碼圖片 的圖片。
思索了一下 封面圖片和二維碼圖片讓后台給接口得到地址, 主要還是找個方式得到一個包含這些內容的圖片。於是就想能不能將View轉化成bitmap對象
然后就走了一遍各個前輩的路 整理了下原理和思路。
根據產品的需求 我要實現的步驟 把所有需要的集合在一個View里 —— View轉化成bitmap —— 分享出去(第三方分享bitmap對象)
接着搞個demo 實驗一下
要轉化的view 大致長這樣
view_photo.xml
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
- <ScrollView
- android:id="@+id/textView"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
- <!--封面圖片 -->
- <ImageView
- android:layout_width="400dp"
- android:layout_height="400dp"
- android:background="@drawable/ic_launcher"
- android:layout_gravity="center_horizontal" />
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="文字 文字"
- android:layout_gravity="center_horizontal" />
- <Button
- android:id="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="假裝是二維碼圖片"
- android:layout_gravity="center_horizontal" />
- </LinearLayout>
- </ScrollView>
- </LinearLayout>
最關鍵的View轉bitmap
好像是有兩種方法
DrawingCache方法:
如果使用DrawingCache,則對要截圖的View有一個要求:View本身已經顯示在界面上。如果View沒有添加到界面上或者沒有顯示(繪制)過,則buildDrawingCache會失敗。這種方式比較適合對應用界面或者某一部分的截圖。步驟很簡單:
- view.setDrawingCacheEnabled(true);
- view.buildDrawingCache(); //啟用DrawingCache並創建位圖
- Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache()); //創建一個DrawingCache的拷貝,因為DrawingCache得到的位圖在禁用后會被回收
- view.setDrawingCacheEnabled(false); //禁用DrawingCahce否則會影響性能
View.draw方法:
這個也很簡單 view對象傳進去.draw()就行
- private Bitmap loadBitmapFromView(View v) {
- int w = v.getWidth();
- int h = v.getHeight();
- Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(bmp);
- c.drawColor(Color.WHITE);
- /** 如果不設置canvas畫布為白色,則生成透明 */
- v.layout(0, 0, w, h);
- v.draw(c);
- return bmp;
- }
然而此時適用於需要截圖的View並沒有添加到界面上,
可能是通過java代碼創建的或者inflate創建的,(我需求的情況就適用於這種。分享的時候生成bitmap對象但不顯示在界面中)
此時調用DrawingCache方法是獲取不到位圖的。
因為View在添加到容器中之前並沒有得到實際的大小(如果LayoutWidth是MatchParent,它還沒有Parent…)
,所以首先需要指定View的大小:
先得到
- DisplayMetrics metric = new DisplayMetrics();
- getWindowManager().getDefaultDisplay().getMetrics(metric);
- int width = metric.widthPixels; // 屏幕寬度(像素)
- int height = metric.heightPixels; // 屏幕高度(像素)
- View view = LayoutInflater.from(this).inflate(R.layout.view_photo, null, false);
- layoutView(view, width, height);//去到指定view大小的函數
- //然后View和其內部的子View都具有了實際大小,也就是完成了布局,相當與添加到了界面上。接着就可以創建位圖並在上面繪制了:
- private void layoutView(View v, int width, int height) {
- // 指定整個View的大小 參數是左上角 和右下角的坐標
- v.layout(0, 0, width, height);
- int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
- int measuredHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);
- /** 當然,measure完后,並不會實際改變View的尺寸,需要調用View.layout方法去進行布局。
- * 按示例調用layout函數后,View的大小將會變成你想要設置成的大小。
- */
- v.measure(measuredWidth, measuredHeight);
- v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
- }
這里我有點不懂后面函數的取值。在自定義view里
onMeasure()里有根據MeasureSpec.getMode()的類型來准確得到設置view的長寬
.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);卻貌似取了 自適應和前一個int參數的最小值。
后面我發現有可能合成出超出屏幕的長圖,就直接吧前一個int參數賦值成一個很大的數字。。。
上整個demo的代碼
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="com.example.aa.MainActivity" >
- <Button
- android:id="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text=" button" />
- <ImageView
- android:id="@+id/aaa"
- android:layout_below="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
- </RelativeLayout>
MainActivity
- public class MainActivity extends Activity {
- ImageView aaa ;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- DisplayMetrics metric = new DisplayMetrics();
- getWindowManager().getDefaultDisplay().getMetrics(metric);
- int width = metric.widthPixels; // 屏幕寬度(像素)
- int height = metric.heightPixels; // 屏幕高度(像素)
- View view = LayoutInflater.from(this).inflate(R.layout.view_photo, null, false);
- layoutView(view, width, height);
- final ScrollView tv = (ScrollView) view.findViewById(R.id.textView);
- aaa = (ImageView) findViewById(R.id.aaa);
- final Runnable runnable = new Runnable() {
- @Override
- public void run() {
- viewSaveToImage(tv);
- }
- };
- Button button = (Button) findViewById(R.id.button);
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- new Handler().post(runnable);
- }
- });
- }
- //然后View和其內部的子View都具有了實際大小,也就是完成了布局,相當與添加到了界面上。接着就可以創建位圖並在上面繪制了:
- private void layoutView(View v, int width, int height) {
- // 整個View的大小 參數是左上角 和右下角的坐標
- v.layout(0, 0, width, height);
- int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
- int measuredHeight = View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.AT_MOST);
- /** 當然,measure完后,並不會實際改變View的尺寸,需要調用View.layout方法去進行布局。
- * 按示例調用layout函數后,View的大小將會變成你想要設置成的大小。
- */
- v.measure(measuredWidth, measuredHeight);
- v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
- }
- public void viewSaveToImage(View view) {
- Log.e("ssh","a");
- /**
- * View組件顯示的內容可以通過cache機制保存為bitmap
- * 我們要獲取它的cache先要通過setDrawingCacheEnable方法把cache開啟,
- * 然后再調用getDrawingCache方法就可 以獲得view的cache圖片了
- * 。buildDrawingCache方法可以不用調用,因為調用getDrawingCache方法時,
- * 若果 cache沒有建立,系統會自動調用buildDrawingCache方法生成cache。
- * 若果要更新cache, 必須要調用destoryDrawingCache方法把舊的cache銷毀,才能建立新的。
- */
- // view.setDrawingCacheEnabled(true);
- // view.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
- //設置繪制緩存背景顏色
- // view.setDrawingCacheBackgroundColor(Color.WHITE);
- // 把一個View轉換成圖片
- Bitmap cachebmp = loadBitmapFromView(view);
- aaa.setImageBitmap(cachebmp);//直接展示轉化的bitmap
- //保存在本地 產品還沒決定要不要保存在本地
- FileOutputStream fos;
- try {
- // 判斷手機設備是否有SD卡
- boolean isHasSDCard = Environment.getExternalStorageState().equals(
- android.os.Environment.MEDIA_MOUNTED);
- if (isHasSDCard) {
- // SD卡根目錄
- File sdRoot = Environment.getExternalStorageDirectory();
- Log.e("ssh",sdRoot.toString());
- File file = new File(sdRoot, "test.png");
- fos = new FileOutputStream(file);
- } else
- throw new Exception("創建文件失敗!");
- //壓縮圖片 30 是壓縮率,表示壓縮70%; 如果不壓縮是100,表示壓縮率為0
- cachebmp.compress(Bitmap.CompressFormat.PNG, 90, fos);
- fos.flush();
- fos.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- view.destroyDrawingCache();
- }
- private Bitmap loadBitmapFromView(View v) {
- int w = v.getWidth();
- int h = v.getHeight();
- Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(bmp);
- c.drawColor(Color.WHITE);
- /** 如果不設置canvas畫布為白色,則生成透明 */
- v.layout(0, 0, w, h);
- v.draw(c);
- return bmp;
- }
- }
demo轉化成結果的bitmap和圖片
