主頁 > 移動端開發 > Android進階之路 - 花樣百出的截屏需求

Android進階之路 - 花樣百出的截屏需求

2021-08-13 07:50:45 移動端開發

最近又遇到了截圖相關的需求,聯合之前的截圖需求后,抽時間整理了一下截屏的常見需求,特此記錄 ~

關聯篇

  • 花樣百出的截屏需求
  • View、Activity實時截圖、截屏
  • 專案全域動態實時截屏,獲取用戶當前操作行為

針對每個人不同的需求,可采用不同的方法 - - ~

        • 截取當前螢屏界面(activity)
        • 截取某個控制元件或區域(view)
        • 截取長屏
          • ScrollView 實作截屏
          • ListView實作截屏
        • WebView實作截屏
        • 區域、全域實時截屏
        • 圖片壓縮
        • 截圖、壓縮
        • 保存圖片到本地
        • 截屏擴展
          • 截取帶導航欄的整個螢屏
          • 截取非含當前應用的螢屏部分(最佳官方方案)

截取當前螢屏界面(activity)

一般呼叫對應方法時出現呼叫不到的場景時,我們需要根據提示進行修改,這里應該需要通過activity進行方法呼叫 ~

別人的方法

    public Bitmap screen() {
        View dView = getWindow().getDecorView();
        dView.setDrawingCacheEnabled(true);
        dView.buildDrawingCache();
        return Bitmap.createBitmap(dView.getDrawingCache());
    }

我的方法 - activity截圖

    //缺點:無法截取WebView頁面,截屏后是白屏!
    public static Bitmap capture(Activity activity) {
        activity.getWindow().getDecorView().setDrawingCacheEnabled(true);
        Bitmap bmp = activity.getWindow().getDecorView().getDrawingCache();
        return bmp;
    }

截圖并保存

    public void screen() {
        View dView = getWindow().getDecorView();
        dView.setDrawingCacheEnabled(true);
        dView.buildDrawingCache();
        Bitmap bitmap = Bitmap.createBitmap(dView.getDrawingCache());
        if(bitmap !=null ) {
            try {
                // 獲取內置SD卡路徑
                String sdCardPath = Environment.getExternalStorageDirectory().getPath();
                // 圖片檔案路徑(自行定義)
                String filePath = sdCardPath + File.separator +"screenshot.png";
                File file =new File(filePath);
                FileOutputStream os = new FileOutputStream(file);
                bitmap.compress(Bitmap.CompressFormat.PNG,100, os);
                os.flush();
                os.close();
            } catch(Exception e) {
            }
        }
    }

截取某個控制元件或區域(view)

可作用于根view或某個控制元件view

  • 簡潔版
	/**
     * 截取指定View為圖片
     */
    public static Bitmap captureView(View view) throws Throwable {
        Bitmap bm = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
        view.draw(new Canvas(bm));
        return bm;
    }

截圖并保存

  public void captureView(View view) {
  		//圖片存盤路徑,可以不關注
        String imgPath = "/sdcard/test.png";
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();
        Bitmap bitmap = view.getDrawingCache();
        //圖片存盤部分可以不關注
        if (bitmap != null) {
            try {
                FileOutputStream out = new FileOutputStream(imgPath);
                bitmap.compress(Bitmap.CompressFormat.PNG, 100,
                        out);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
  • 繁瑣版
   public void screen(View view){
        View dView = view;
        dView.setDrawingCacheEnabled(true);
        dView.buildDrawingCache();
        //以下倆種方式,一種是取圖片快取,一種是重新繪制,可自行調節嘗試
        //Bitmap bitmap = Bitmap.createBitmap(dView.getDrawingCache());
        Bitmap bitmap = Bitmap.createBitmap(dView.getWidth(), dView.getHeight(), Bitmap.Config.ARGB_8888);
        //使用Canvas,呼叫自定義view控制元件的onDraw方法,繪制圖片
        Canvas canvas =new Canvas(bitmap);
        dView.draw(canvas);
    }
  • view截圖(有人說截取view轉bitmap后為null,可用這種方式清理快取,未嘗試
    public static Bitmap getViewBp(View v) {
        if (null == v) {
            return null;
        }
        v.setDrawingCacheEnabled(true);
        v.buildDrawingCache();
        if (Build.VERSION.SDK_INT >= 11) {
            v.measure(View.MeasureSpec.makeMeasureSpec(v.getWidth(),
                    View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(
                    v.getHeight(), View.MeasureSpec.EXACTLY));
            v.layout((int) v.getX(), (int) v.getY(),
                    (int) v.getX() + v.getMeasuredWidth(),
                    (int) v.getY() + v.getMeasuredHeight());
        } else {
            v.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
            v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
        }
        Bitmap b = Bitmap.createBitmap(v.getDrawingCache(), 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());

        v.setDrawingCacheEnabled(false);
        v.destroyDrawingCache();
        return b;
    }
  • view截圖(有人說截取view轉bitmap后為null,可用這種方式清理快取,未嘗試
	public static Bitmap convertViewToBitmap(View view) {
        view.setDrawingCacheEnabled(true);
        view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
        view.buildDrawingCache();
        Bitmap bitmap = view.getDrawingCache();
        view.setDrawingCacheEnabled(false);
        return bitmap;
 	}

截取長屏

? 截取長屏其實原理就是截取整個ScrollView或者ListView的視圖,因此實作原理跟上面中提到的截取某個控制元件的View基本一致,

ScrollView 實作截屏
		/*
         * 截取scrollview的螢屏
         **/
     public static Bitmap getScrollViewBitmap(ScrollView scrollView) {
         int h = 0;
         Bitmap bitmap;
         for  ( int i = 0 ; i < scrollView.getChildCount(); i++) {
         		h += scrollView.getChildAt(i).getHeight();
   		 }
 
         // 創建對應大小的bitmap
         bitmap = Bitmap.createBitmap(scrollView.getWidth(), h,Bitmap.Config.ARGB_8888);
         final Canvas canvas = new Canvas(bitmap);
         scrollView.draw(canvas);  
         return bitmap;
      }
ListView實作截屏
   /**
     * 截圖listview
     **/
     public static Bitmap getListViewBitmap(ListView listView) {
         int h = 0;
         Bitmap bitmap;
         // 獲取listView實際高度
         for  (int  i = 0 ; i < listView.getChildCount(); i++) {
        	 h += listView.getChildAt(i).getHeight();
       	 }
         Log.d(TAG,   "實際高度:"  + h);  
         Log.d(TAG,   "list 高度:"  + listView.getHeight());
         
         // 創建對應大小的bitmap
         bitmap = Bitmap.createBitmap(listView.getWidth(), h,       
         Bitmap.Config.ARGB_8888); 
         final  Canvas canvas =  new Canvas(bitmap);
         listView.draw(canvas);    
         return bitmap;
    }

WebView實作截屏

    //這是webview的,利用了webview的api
    private static Bitmap captureWebView(WebView webView) {
         Picture snapShot = webView.capturePicture();
         Bitmap bmp = Bitmap.createBitmap(snapShot.getWidth(),     
         snapShot.getHeight(), Bitmap.Config.ARGB_8888);   
         Canvas canvas = new Canvas(bmp);
         snapShot.draw(canvas);    
         return bmp;  
    }

區域、全域實時截屏

關于應用內全域截屏的需求,我在 如何優雅的實時獲取用戶操作界面 中詳細的解釋了使用方式


在日常開發中,一般截屏后的圖片,我們都會進行壓縮、保存,這里一并進行記錄

圖片壓縮

 /**
     * 壓縮圖片
     *
     * @param bgimage
     * @param newWidth
     * @param newHeight
     * @return
     */
    public static Bitmap zoomImage(Bitmap bgimage, double newWidth, double newHeight) {
        // 獲取這個圖片的寬和高
        float width = bgimage.getWidth();
        float height = bgimage.getHeight();
        // 創建操作圖片用的matrix物件
        Matrix matrix = new Matrix();
        // 計算寬高縮放率
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        // 縮放圖片動作
        //matrix.postScale(scaleWidth, scaleHeight);//TODO 因為寬高不確定的因素,所以不縮放
        Bitmap bitmap = Bitmap.createBitmap(bgimage, 0, 0, (int) width,
                (int) height, matrix, true);
        return bitmap;
    }

截圖、壓縮

	 	Bitmap bitmap = null;
          try {
                 bitmap = captureView(mShareBackgroundSign);
              } catch (Throwable throwable) {
                    throwable.printStackTrace();
              }
           //圖片壓縮,加快使用速度~
           zoomImage(bitmap, 720, 1280);

保存圖片到本地

注意:切記適配6.0、7.0

public static void savePhotoToSDCard(Bitmap photoBitmap, String path, String photoName) {
        if (checkSDCardAvailable()) {
            File dir = new File(path);
            if (!dir.exists()) {
                dir.mkdirs();
            }
 
            File photoFile = new File(path, photoName + ".png");
            FileOutputStream fileOutputStream = null;
            try {
                fileOutputStream = new FileOutputStream(photoFile);
                if (photoBitmap != null) {
                    if (photoBitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)) {
                        fileOutputStream.flush();
                    }
                }
            } catch (FileNotFoundException e) {
                photoFile.delete();
                e.printStackTrace();
            } catch (IOException e) {
                photoFile.delete();
                e.printStackTrace();
            } finally {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

截屏擴展

以下部分其實我并未在有限的開發中使用到,僅當做個人興趣的知識延伸

截取帶導航欄的整個螢屏

采用adb命令方式截屏的操作,屈指可數,可當做知識擴展,沒有必要深讀

  • 優點:只需在代碼中執行截屏的命令即可,可根據需求封裝一個方法,傳入保存的路徑和檔案名即可;
  • 缺點:手機需要獲取ROOT權限,呼叫命令之前需要先請求su獲取ROOT權限;

adb 命令:這里指的不是連接電腦進行adb操控,而是在App內部實作adb命令的操控

在APK中呼叫 "adb shell screencap -p filepath" 命令

注意:該命令讀取系統的framebuffer,需要獲得系統權限

  1. 在AndroidManifest.xml檔案中添加
 <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
  1. 修改APK為系統權限,將APK放到原始碼中編譯, 修改Android.mk
 LOCAL_CERTIFICATE:= platform
        public void takeScreenShot () {
            String mSavedPath = Environment.getExternalStorageDirectory() + File.separator + "screenshot.png";
            try {
                Runtime.getRuntime().exec("screencap -p " + mSavedPath);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

利用系統的隱藏API,實作Screenshot,這部分代碼是系統隱藏的,需要在原始碼下編譯,

  1. 修改Android.mk, 添加系統權限
 LOCAL_CERTIFICATE := platform
  1. 修改AndroidManifest.xml 檔案,添加權限
 <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />

Android本地編程(Native Programming)讀取framebuffer命令列,框架的截屏功能是通過framebuffer來實作的

framebuffer介紹

幀緩沖(framebuffer)是Linux為顯示設備提供的一個介面,把顯存抽象后的一種設備,他允許上層應用程式在圖形模式下直接對顯示緩沖區進行 讀寫操作,這種操作是抽象的,統一的,用戶不必關心物理顯存的位置、換頁機制等等具體細節,這些都是由Framebuffer設備驅動來完成的,
linux FrameBuffer 本質上只是提供了對圖形設備的硬體抽象,在開發者看來,FrameBuffer 是一塊顯示快取,往顯示快取中寫入特定格式的資料就意味著向螢屏輸出內容,所以說FrameBuffer就是一塊白板,例如對于初始化為16 位色的FrameBuffer 來說, FrameBuffer中的兩個位元組代表螢屏上一個點,從上到下,從左至右,螢屏位置與記憶體地址是順序的線性關系,
幀快取有個地址,是在記憶體里,我們通過不停的向frame buffer中寫入資料, 顯示控制器就自動的從frame buffer中取資料并顯示出來,全部的圖形都共享記憶體中同一個幀快取,

android截屏實作思路

Android系統是基于Linux內核的,所以也存在framebuffer這個設備,我們要實作截屏的話只要能獲取到framebuffer中的資料,然后把資料轉換成圖片就可以了,android中的framebuffer資料是存放在 /dev/graphics/fb0 檔案中的,所以我們只需要來獲取這個檔案的資料就可以得到當前螢屏的內容,
現在我們的測驗代碼運行時候是通過RC(remote controller)方式來運行被測應用的,那就需要在PC機上來訪問模擬器或者真機上的framebuffer資料,這個的話可以通過android的ADB命令來實作,
各大手機自帶的按鍵組合進行截屏,Android原始碼中對按鍵的捕獲位于檔案PhoneWindowManager.java(alps\frameworks\base\policy\src\com\android\internal\policy\impl)中,這個類處理所有的鍵盤輸入事件,其中函式interceptKeyBeforeQueueing()會對常用的按鍵做特殊處理,

截取非含當前應用的螢屏部分(最佳官方方案)

Android 在5.0 之后支持了實時錄屏的功能;通過實時錄屏我們可以拿到截屏的影像,同時可以通過在Service中處理實作后臺的錄屏(具體的類講解大家自行網上查閱)

類似使用手機的系統截屏(音量下鍵+電源鍵),針對的并非一個view,而是整個螢屏,在呼叫這個服務的時候,會彈出一個權限確認的彈框;同時需注意,這一方法只能在Android 5.0的系統設備上使用,

詳細步驟

  1. 初始化一個MediaProjectionManager
 MediaProjectionManager mMediaProjectionManager = (MediaProjectionManager)getApplication().getSystemService(Context.MEDIA_PROJECTION_SERVICE); 
  1. 創建intent,并啟動Intent(注意:這里是startActivityForResult)
 startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION); 
  1. 在onActivityResult中拿到Mediaprojection
 mResultCode = resultCode;
 mResultData = data;
 mMediaProjection = mMediaProjectionManager.getMediaProjection(mResultCode, mResultData);
  1. 設定VirtualDisplay 將影像和展示的View關聯起來;一般來說我們會將影像展示到SurfaceView,這里為了為了便于拿到截圖,我們使用ImageReader,他內置有SurfaceView
   mImageReader = ImageReader.newInstance(windowWidth, windowHeight, 0x1 ,2); 
   //ImageFormat.RGB_565
   mVirtualDisplay = mMediaProjection.createVirtualDisplay("screen-mirror" ,windowWidth, windowHeight, mScreenDensity,DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,      
   mImageReader.getSurface(), null ,  null);
  1. 通過ImageReader拿到截圖
   strDate = dateFormat.format(new  java.util.Date());
   nameImage = pathImage+strDate+ ".png";
   Image image = mImageReader.acquireLatestImage();
   int  width = image.getWidth();
   int  height = image.getHeight();
   final Image.Plane[] planes = image.getPlanes();
   final ByteBuffer buffer = planes[0].getBuffer();
   int pixelStride = planes[0].getPixelStride();
   int  rowStride = planes[0].getRowStride();
   int rowPadding = rowStride - pixelStride * width;
   Bitmap bitmap = Bitmap.createBitmap(width+rowPadding/pixelStride, height, Bitmap.Config.ARGB_8888);
   bitmap.copyPixelsFromBuffer(buffer);
   bitmap = Bitmap.createBitmap(bitmap, 0, 0 ,width, height);
   image.close();
  1. 注意截屏之后要及時關閉VirtualDisplay ,因為VirtualDisplay 是十分消耗記憶體和電量的
  if(mVirtualDisplay == null) {
		return ;
  }	
  mVirtualDisplay.release();
  mVirtualDisplay = null;

常用步驟

  1. 在Activity中開啟截屏服務
  • 鏈式
        if (Build.VERSION.SDK_INT >= 21) {
            startActivityForResult(((MediaProjectionManager) getSystemService("media_projection")).createScreenCaptureIntent(), 1);
        } else {
            Log.e("TAG", "版本過低,無法截屏");
        }
  • 平常
 final MediaProjectionManager projectionManager = (MediaProjectionManager)
                getSystemService(Context.MEDIA_PROJECTION_SERVICE);
 Intent intent = projectionManager.createScreenCaptureIntent();
 startActivityForResult(intent, REQUEST_CODE);
  1. 重寫onActivityResult方法
  • 鏈式 - 回傳結果
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 1 && data != null) {
            parseData(data);
        }
    }
    private void parseData(Intent data){
        MediaProjection mMediaProjection = (MediaProjectionManager).getSystemService(
                Context.MEDIA_PROJECTION_SERVICE).getMediaProjection(Activity.RESULT_OK,data);
        ImageReader mImageReader = ImageReader.newInstance(
                getScreenWidth(),
                getScreenHeight(),
                PixelFormat.RGBA_8888,1);
        VirtualDisplay mVirtualDisplay = mMediaProjection.createVirtualDisplay("screen-mirror",
                getScreenWidth(),
                getScreenHeight(),
                Resources.getSystem().getDisplayMetrics().densityDpi,
                DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                mImageReader.getSurface(), null, null);
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Image image = mImageReader.acquireLatestImage();
                // TODO 將image保存到本地即可
            }
        }, 300);
        mVirtualDisplay.release();
        mVirtualDisplay = null;
    }
  • 平常 - 回傳結果
 	@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        handleScreenShotIntent(resultCode, data);
    }
    
    private void handleScreenShotIntent(int resultCode, Intent data) {
        onScreenshotTaskBegan();
        final MediaProjectionManager projectionManager = (MediaProjectionManager)
                getSystemService(Context.MEDIA_PROJECTION_SERVICE);
        final MediaProjection mProjection = projectionManager.getMediaProjection(resultCode, data);
        Point size = Utils.getScreenSize(this);
        final int mWidth = size.x;
        final int mHeight = size.y;
        final ImageReader mImageReader = ImageReader.newInstance(mWidth, mHeight, PixelFormat
                .RGBA_8888, 2);
        final VirtualDisplay display = mProjection.createVirtualDisplay("screen-mirror", mWidth,
                mHeight, DisplayMetrics.DENSITY_MEDIUM,
                DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION, mImageReader.getSurface(),
                null, null);

        mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader mImageReader) {
                Image image = null;
                try {
                    image = mImageReader.acquireLatestImage();
                    if (image != null) {
                        final Image.Plane[] planes = image.getPlanes();
                        if (planes.length > 0) {
                            final ByteBuffer buffer = planes[0].getBuffer();
                            int pixelStride = planes[0].getPixelStride();
                            int rowStride = planes[0].getRowStride();
                            int rowPadding = rowStride - pixelStride * mWidth;
                            // create bitmap
                            Bitmap bmp = Bitmap.createBitmap(mWidth + rowPadding / pixelStride,
                                    mHeight, Bitmap.Config.ARGB_8888);
                            bmp.copyPixelsFromBuffer(buffer);
                            Bitmap croppedBitmap = Bitmap.createBitmap(bmp, 0, 0, mWidth, mHeight);
							//保存圖片
                            saveBitmap(croppedBitmap);
                            if (croppedBitmap != null) {
                                croppedBitmap.recycle();
                            }
                            if (bmp != null) {
                                bmp.recycle();
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (image != null) {
                        image.close();
                    }
                    if (mImageReader != null) {
                        mImageReader.close();
                    }
                    if (display != null) {
                        display.release();
                    }
                    mImageReader.setOnImageAvailableListener(null, null);
                    mProjection.stop();
                    onScreenshotTaskOver();
                }
            }
        }, getBackgroundHandler());
    }

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/293381.html

標籤:其他

上一篇:Compose Multiplatform 正式官宣,與 Flutter 遲早必有一戰?

下一篇:Kotlin 行內函式let,with,run,apply,also區別和用法

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more