获取View的Bitmap以及ViewPager使用

获取View的Bitmap几种方法:

一、创建一个新的空Bitmap,然后再根据它来创建一个Canvas,最后调用View的draw方法将View画到Canvas上
不建议采用该方式


    public Bitmap createViewBitmap(View v) {
        Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(),
                Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        v.layout(0, 0, v.getWidth(), v.getHeight());  
        v.draw(canvas);
        return bitmap;

若view没有显示出来,则要去获取宽高,更完整的参考:https://blog.csdn.net/dongbaoming/article/details/54172801

性能优化:
1 若对生成的图片没有透明度分量的要求,则可以将生成图片改为RGB565 而非ARGB8888


二、View的DrawingCache 方法
6.0以及以下版本建议方式 耗时大概0.012s这样 因为取的是view的缓存会快一些

若想获取可见view的bitmap 则view设置为 getActivity().getWindow().getDecorView()获取decorView即可

这种方式获取不到surfaceView,因为SurfaceView的绘制其实是我们自己完成的,分为Surface和View 而通过getDrawingcache的方式获取的是view的内容

这种方案的原理是 获取该view在内存中的缓存

/**
     * 获取 View 的缓存视图
     *
     * @param view
     * @return
     */
    private fun getCacheBitmapFromView(view: View): Bitmap? {
  	    // 开启view的bitmap cache
        view.isDrawingCacheEnabled = true
        // 获取view的bitmap cache
        val drawingCache = view.drawingCache // getDrawingCache()
        val bitmap: Bitmap?
        if (drawingCache != null) {
            //创建一个DrawingCache的拷贝,因为DrawingCache得到的位图在禁用后会被回收
            bitmap = Bitmap.createBitmap(drawingCache)
            // 将cache关闭 节省内存
            view.isDrawingCacheEnabled = false
            view.destroyDrawingCache()
        } else {
            bitmap = null
        }
        return bitmap
    }

默认 getDrawingCache 得到的bitmap 为 ARGB8888

问题: getDrawingCache 返回的为空
原因: 1 View没有显示到界面上,获取的 VIew的宽高为空


三、 PixelCopy.request
android7.0以上推荐该方式 耗时大概0.023s这样 因为是像素级拷贝 会慢一些
该方式主要通过像素级的拷贝,因此可以获取任意view的bitmap

1 获取surfaceView的bitmap android7.0以上

final Bitmap bitmap = Bitmap.createBitmap(surfaceView.getWidth(), surfaceView.getHeight(), Bitmap.Config.ARGB_8888);
                    final HandlerThread handlerThread = new HandlerThread(GetLastFrameBitmapUtil.class.getSimpleName());
                    handlerThread.start();
                    PixelCopy.request(surfaceView, bitmap, new PixelCopy.OnPixelCopyFinishedListener() {
                        @Override
                        public void onPixelCopyFinished(int copyResult) {
                            try {
                                if (copyResult == PixelCopy.SUCCESS && bitmap != null) {
                                    Logger.t(TAG).d("interactKP: get >6.0 SurfaceView bitmap success.");
                                } else {
                                    Logger.t(TAG).e("interactKP: get >6.0 SurfaceView bitmap failed.");
                                }
                                handlerThread.quitSafely();
                            } catch (Throwable t) {
                                Logger.t(TAG).e("interactKP: get >6.0 SurfaceView throwable = " + t);
                            }
                        }
                    }, new Handler(handlerThread.getLooper()));

2 获取普通view的内容 android7.0以上

private var mBitmap: Bitmap? = null
private var mHandlerThread = HandlerThread(TestActivity::class.java.simpleName)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // 获取layout的位置
            val location = IntArray(2)
            rl_rl.getLocationInWindow(location)
            PixelCopy.request(window,
                    Rect(location[0], location[1], location[0] + rl_rl.width, location[1] + rl_rl.height),
                    mBitmap!!, { copyResult ->
                try {
                    if (copyResult == PixelCopy.SUCCESS) {
                        Log.e("TestActivity", "interactKP: get >6.0 bitmap success.")
                    } else {
                        Log.e("TestActivity", "interactKP: get >6.0 bitmap failed.")
                    }
                } catch (t: Throwable) {
                    Log.e("TestActivity", "interactKP: get >6.0 throwable = $t")
                }
            }, Handler(mHandlerThread.looper))
        }

四、6.0以下获取surfaceView的bitmap
(1) 在SurfaceView中实时输出bitmap 但是要注意好内存资源的占用和回收

Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 
    Canvas c = new Canvas(b);
    yourSurfaceView.draw(c);

(2)root的手机 通过framebuffer获取
(3)改造成TextureView
通过

val bitmap = Bitmap.createBitmap(it.width, it.height, Bitmap.Config.ARGB_8888)
textureView.getBitmap(bitmap)
// 获取直接获取FrameBuffer的glReadPixels()?

即可获取

(4) GLSurfaceView
https://blog.csdn.net/afei__/article/details/51614375
https://stackoverflow.com/questions/27817577/android-take-screenshot-of-surface-view-shows-black-screen/27824250#27824250

(5)通过adb shell方式获取
截取带导航栏的整个屏幕 可以截取手机任何界面任何view

adb shell screencap 路径.png

要用png保存图片 jpg的打不开

该命令读取系统的framebuffer,需要获得系统权限:
(1). 在AndroidManifest.xml文件中添加

<uses-permissionandroid:name="android.permission.READ_FRAME_BUFFER"/>

(2)得使用系统权限 shardUserId="android.uid.system"

(6)截取非含当前应用的屏幕部分
先开启录屏 通过 MediaProjectionManager 然后将画面保存

framebuffer是linux为显示设备提供的接口 是一个显示缓冲区 可以进行读写操作
android中的framebuffer数据是存放在 /dev/graphics/fb0 文件中的


alibaba viewPager库:https://github.com/alibaba/UltraViewPager

一、build.gradle

//gradle
    implementation('com.alibaba.android:ultraviewpager:1.0.7.7@aar') {
        transitive = true
    }

二、加载UltraPager和Adapter

        val ultraViewPager = findViewById<View>(R.id.ultra_viewpager) as UltraViewPager
        // 有水平和垂直方向
        ultraViewPager.setScrollMode(UltraViewPager.ScrollMode.HORIZONTAL)
        // 设置多屏显示 1.0f为单屏 如0.5则是左右两边都留图片的一半显示
        ultraViewPager.setMultiScreen(1.0f)
        // 设置 转换的动态效果,有两种
        ultraViewPager.setPageTransformer(true, UltraDepthScaleTransformer())
        //设定页面循环播放
        ultraViewPager.setInfiniteLoop(false)
        //设定页面自动切换  间隔2秒
        //mUltraViewPager.setAutoScroll(2000)
        // UltraViewPager的高度会自动调整到child view的高度
        //ultraViewPager.setAutoMeasureHeight(true)
        // 设置后会以此宽高比调整child view的高度
        //ultraViewPager.setItemRatio(1.0)
        // 以设定的宽高比来绘制UltraViewPager
        //ultraViewPager.setRatio(1.5f)


/** initialize UltraPagerAdapter,and add child view to UltraViewPager */
        val adapter = UltraPagerAdapter(this)
        ultraViewPager.adapter = adapter

三、UltraPagerAdapter类:

这里主要 实现了
加载3个自定义的layout
设置布局的点击监听
更改某个TextView的字体
获取View的缓存视图

class UltraPagerAdapter(private val context: Context) : PagerAdapter(), View.OnClickListener {

    private val TAG = "UltraPagerAdapter"

    companion object {
        @JvmField
        var viewCacheBitmap :Bitmap ?=null
    }
    override fun getCount(): Int {
        return 3
    }

    override fun isViewFromObject(view: View, obj: Any): Boolean {
        return view === obj
    }
    /** 加载3个自定义的layout */
    override fun instantiateItem(container: ViewGroup, position: Int): Any {
        val layoutRelativeLayout01 = LayoutInflater.from(container.context).inflate(R.layout.template01, null)
        val layoutRelativeLayout02 = LayoutInflater.from(container.context).inflate(R.layout.template02, null)
        val layoutRelativeLayout03 = LayoutInflater.from(container.context).inflate(R.layout.template03, null)

        val relativeLayout01 = layoutRelativeLayout01.findViewById(R.id.template_01) as RelativeLayout
        val relativeLayout02 = layoutRelativeLayout02.findViewById(R.id.template_02) as RelativeLayout
        val relativeLayout03 = layoutRelativeLayout03.findViewById(R.id.template_03) as RelativeLayout

        val tvDay01 = layoutRelativeLayout01.findViewById(R.id.tv_template01_day) as TextView
        val tvDay02 = layoutRelativeLayout02.findViewById(R.id.tv_template02_day) as TextView
        val tvDay03 = layoutRelativeLayout03.findViewById(R.id.tv_template03_day) as TextView

        /** 设置字体样式 */
        val textFont = Typeface.createFromAsset(context.assets, "fonts/CharlemagneStd-Bold.otf")
        if (textFont != null) {
            tvDay01.typeface = textFont
            tvDay02.typeface = textFont
            tvDay03.typeface = textFont
        } else {
            Logger.t(TAG).e("字体库不存在")
        }

        // 设置监听
        relativeLayout01.setOnClickListener(this)
        relativeLayout02.setOnClickListener(this)
        relativeLayout03.setOnClickListener(this)
        /** 加载界面 */
        when (position) {
            0 -> {
                container.addView(relativeLayout01)
                return relativeLayout01
            }
            1 -> {
                container.addView(relativeLayout02)
                return relativeLayout02
            }
            2 -> {
                container.addView(relativeLayout03)
                return relativeLayout03
            }
        }
        return relativeLayout01
    }

    override fun destroyItem(container: ViewGroup, position: Int, obj: Any) {
        val view = obj as RelativeLayout
        container.removeView(view)
    }

    override fun onClick(view: View?) {
        if (view != null) {
            viewCacheBitmap = getCacheBitmapFromView(view)
            Logger.t(TAG).e("viewCacheBitmap width" + viewCacheBitmap?.width
                    + "  viewCacheBitmap height" + viewCacheBitmap?.height)
        }else{
            Logger.t(TAG).e("View is null")
        }

        val sharedPicDialog = SharedPicDialog(context)
        sharedPicDialog.show()

    }


    /**
     * 获取 View 的缓存视图
     *
     * @param view
     * @return
     */
    private fun getCacheBitmapFromView(view: View): Bitmap? {
        val drawingCacheEnabled = true
        view.isDrawingCacheEnabled = drawingCacheEnabled
        view.buildDrawingCache(drawingCacheEnabled)
        val drawingCache = view.drawingCache
        val bitmap: Bitmap?
        if (drawingCache != null) {
            bitmap = Bitmap.createBitmap(drawingCache)
            view.isDrawingCacheEnabled = false
        } else {
            bitmap = null
        }
        return bitmap
    }
}

问题1 系统viewpager滑动到第二页,再屏幕旋转,然后关闭viewpager(View.GONE)再屏幕旋转回去,再打开ViewPager(View.VISIBLE) 此时viewPager的内容位置错位
viewpager 显示的时候 会去屏幕旋转一下,重新layout,但是layout计算的值错误
解决: 使用INVISIBLE代替GONE

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值