美文网首页Android进阶程序员Android进阶之旅
Android 根据View生成图片简易参考

Android 根据View生成图片简易参考

作者: 阿敏其人 | 来源:发表于2017-04-21 22:00 被阅读899次

    一、分类

    开发中,我们有时候需要根据View生成图片。

    本文根据不同情况的View生成图片进行了一些示例,分类如下
    第一种,普通View生成图片(view已经渲染加载到界面上)
    第二种,无中生有,通过java代码创建的或者inflate创建
    第三种,WebView 生成图片
    第四种,ScrollView 生成图片
    第五种,ListView 生成图片
    第六种,RecyclerView 生成图片

    还是图来的直接


    shot.gif

    核心代码

    public  class SimpleUtils {
    
        /**
         * 将 Bitmap 保存到SD卡
         * @param context
         * @param mybitmap
         * @param name
         * @return
         */
        public static boolean saveBitmapToSdCard(Context context, Bitmap mybitmap, String name){
            boolean result = false;
            //创建位图保存目录
            String path = Environment.getExternalStorageDirectory() + "/1000ttt/";
            File sd = new File(path);
            if (!sd.exists()){
                sd.mkdir();
            }
    
            File file = new File(path+name+".jpg");
            FileOutputStream fileOutputStream = null;
            if (!file.exists()){
                try {
                    // 判断SD卡是否存在,并且是否具有读写权限
                    if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                        fileOutputStream = new FileOutputStream(file);
                        mybitmap.compress(Bitmap.CompressFormat.JPEG,100,fileOutputStream);
                        fileOutputStream.flush();
                        fileOutputStream.close();
    
                        //update gallery
                        Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                        Uri uri = Uri.fromFile(file);
                        intent.setData(uri);
                        context.sendBroadcast(intent);
                        Toast.makeText(context, "保存成功", Toast.LENGTH_SHORT).show();
                        result = true;
                    }
                    else{
                        Toast.makeText(context, "不能读取到SD卡", Toast.LENGTH_SHORT).show();
                    }
    
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return result;
        }
    
    
        /**
         * 手动测量摆放View
         * 对于手动 inflate 或者其他方式代码生成加载的View进行测量,避免该View无尺寸
         * @param v
         * @param width
         * @param height
         */
        public static void layoutView(View v, int width, int height) {
            // validate view.width and view.height
            v.layout(0, 0, width, height);
            int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
            int measuredHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
    
            // validate view.measurewidth and view.measureheight
            v.measure(measuredWidth, measuredHeight);
            v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
        }
    
    
        public static int px2dip(Context context, float pxValue) {
            final float scale = context.getResources().getDisplayMetrics().density;
            return (int) (pxValue / scale + 0.5f);
        }
    
    
    
        /**
         * 获取一个 View 的缓存视图
         *  (前提是这个View已经渲染完成显示在页面上)
         * @param view
         * @return
         */
        public static Bitmap getCacheBitmapFromView(View view) {
            final boolean drawingCacheEnabled = true;
            view.setDrawingCacheEnabled(drawingCacheEnabled);
            view.buildDrawingCache(drawingCacheEnabled);
            final Bitmap drawingCache = view.getDrawingCache();
            Bitmap bitmap;
            if (drawingCache != null) {
                bitmap = Bitmap.createBitmap(drawingCache);
                view.setDrawingCacheEnabled(false);
            } else {
                bitmap = null;
            }
            return bitmap;
        }
    
        /**
         *  对ScrollView进行截图
         * @param scrollView
         * @return
         */
        public static Bitmap shotScrollView(ScrollView scrollView) {
            int h = 0;
            Bitmap bitmap = null;
            for (int i = 0; i < scrollView.getChildCount(); i++) {
                h += scrollView.getChildAt(i).getHeight();
                scrollView.getChildAt(i).setBackgroundColor(Color.parseColor("#ffffff"));
            }
            bitmap = Bitmap.createBitmap(scrollView.getWidth(), h, Bitmap.Config.RGB_565);
            final Canvas canvas = new Canvas(bitmap);
            scrollView.draw(canvas);
            return bitmap;
        }
    
    
        /**
         * 对ListView进行截图
         * http://stackoverflow.com/questions/12742343/android-get-screenshot-of-all-listview-items
         */
        public static Bitmap shotListView(ListView listview) {
    
            ListAdapter adapter = listview.getAdapter();
            int itemscount = adapter.getCount();
            int allitemsheight = 0;
            List<Bitmap> bmps = new ArrayList<Bitmap>();
    
            for (int i = 0; i < itemscount; i++) {
    
                View childView = adapter.getView(i, null, listview);
                childView.measure(
                        View.MeasureSpec.makeMeasureSpec(listview.getWidth(), View.MeasureSpec.EXACTLY),
                        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
    
                childView.layout(0, 0, childView.getMeasuredWidth(), childView.getMeasuredHeight());
                childView.setDrawingCacheEnabled(true);
                childView.buildDrawingCache();
                bmps.add(childView.getDrawingCache());
                allitemsheight += childView.getMeasuredHeight();
            }
    
            Bitmap bigbitmap =
                    Bitmap.createBitmap(listview.getMeasuredWidth(), allitemsheight, Bitmap.Config.ARGB_8888);
            Canvas bigcanvas = new Canvas(bigbitmap);
    
            Paint paint = new Paint();
            int iHeight = 0;
    
            for (int i = 0; i < bmps.size(); i++) {
                Bitmap bmp = bmps.get(i);
                bigcanvas.drawBitmap(bmp, 0, iHeight, paint);
                iHeight += bmp.getHeight();
    
                bmp.recycle();
                bmp = null;
            }
    
            return bigbitmap;
        }
    
    
        /**
         * 对RecyclerView进行截图
         * https://gist.github.com/PrashamTrivedi/809d2541776c8c141d9a
         */
        public static Bitmap shotRecyclerView(RecyclerView view) {
            RecyclerView.Adapter adapter = view.getAdapter();
            Bitmap bigBitmap = null;
            if (adapter != null) {
                int size = adapter.getItemCount();
                int height = 0;
                Paint paint = new Paint();
                int iHeight = 0;
                final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
    
                // Use 1/8th of the available memory for this memory cache.
                final int cacheSize = maxMemory / 8;
                LruCache<String, Bitmap> bitmaCache = new LruCache<>(cacheSize);
                for (int i = 0; i < size; i++) {
                    RecyclerView.ViewHolder holder = adapter.createViewHolder(view, adapter.getItemViewType(i));
                    adapter.onBindViewHolder(holder, i);
                    holder.itemView.measure(
                            View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY),
                            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
                    holder.itemView.layout(0, 0, holder.itemView.getMeasuredWidth(),
                            holder.itemView.getMeasuredHeight());
                    holder.itemView.setDrawingCacheEnabled(true);
                    holder.itemView.buildDrawingCache();
                    Bitmap drawingCache = holder.itemView.getDrawingCache();
                    if (drawingCache != null) {
    
                        bitmaCache.put(String.valueOf(i), drawingCache);
                    }
                    height += holder.itemView.getMeasuredHeight();
                }
    
                bigBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), height, Bitmap.Config.ARGB_8888);
                Canvas bigCanvas = new Canvas(bigBitmap);
                Drawable lBackground = view.getBackground();
                if (lBackground instanceof ColorDrawable) {
                    ColorDrawable lColorDrawable = (ColorDrawable) lBackground;
                    int lColor = lColorDrawable.getColor();
                    bigCanvas.drawColor(lColor);
                }
    
                for (int i = 0; i < size; i++) {
                    Bitmap bitmap = bitmaCache.get(String.valueOf(i));
                    bigCanvas.drawBitmap(bitmap, 0f, iHeight, paint);
                    iHeight += bitmap.getHeight();
                    bitmap.recycle();
                }
            }
            return bigBitmap;
        }
    }
    

    核心代码都在上面了,下面开始分析。

    二、普通View

    普通View,在本文中就是已经绘制到界面的View。

    Paste_Image.png

    如上,点击截取View,并且我们会生成图片显示保存到sd卡

    普通View生成图片参考代码

        /**
         * 获取一个 View 的缓存视图
         *  (前提是这个View已经渲染完成显示在页面上)
         * @param view
         * @return
         */
        public static Bitmap getCacheBitmapFromView(View view) {
            final boolean drawingCacheEnabled = true;
            view.setDrawingCacheEnabled(drawingCacheEnabled);
            view.buildDrawingCache(drawingCacheEnabled);
            final Bitmap drawingCache = view.getDrawingCache();
            Bitmap bitmap;
            if (drawingCache != null) {
                bitmap = Bitmap.createBitmap(drawingCache);
                view.setDrawingCacheEnabled(false);
            } else {
                bitmap = null;
            }
            return bitmap;
        }
    

    .
    .
    .
    至于保存Bitmap到sd卡操作,虽然常见一些,代码还是附在顶部工具类了。

    三、代码加载但是未显示在界面的View

    inf.gif

    有一些View,我们是通过代码加载出来的,但是没有加载界面上,我们也可以对这种View生成图片。

    什么?既然没有显示在界面上,那还要加载来干嘛?
    此言差矣,用处还是有的,YY即可。

    如图,按下截图按钮,我们做的主要逻辑如下:

    // 本View是inflate加载而来,不是Activity的xml本身的
            View view = getLayoutInflater().inflate(R.layout.item_group,null);
            ImageView mtv = (ImageView) view.findViewById(R.id.mIv);
            ViewGroup.LayoutParams upPartLayoutParams = mtv.getLayoutParams();
            int upPartMeasureHeight = View.MeasureSpec.makeMeasureSpec(upPartLayoutParams.height, View.MeasureSpec.EXACTLY);
            mtv.setImageDrawable(getResources().getDrawable(R.drawable.ccc));
    
            // 没有显示到界面上的view本身无大小可言,所以我们要手动指定一下
            SimpleUtils.layoutView(mtv,upPartMeasureHeight,upPartMeasureHeight);
            // View生成截图
            Bitmap cacheBitmapFromView = SimpleUtils.getCacheBitmapFromView(mtv);
            mIvResult.setImageBitmap(cacheBitmapFromView);
            // 保存bitmap到sd卡
            SimpleUtils.saveBitmapToSdCard(StyleTwoActivity.this,cacheBitmapFromView,"styleTwo");
    

    .
    .
    .
    代码中,我们看到,我们按下截图,inflate加载一个简单布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="200px"
                  android:layout_height="200px"
        >
    
        <ImageView
            android:id="@+id/mIv"
            android:layout_width="200px"
            android:layout_height="200px"
            android:background="#623512"
            android:text="哇阿斯达撒大声地哈哈哈就是这样的按到"
            android:textColor="#ffffff"
            android:textSize="20sp"
            />
    </LinearLayout>
    

    我们看到,里面就是一个ImagView,我们待会就是要给这个ImageView 设置一张图片,然后对这个View进行生成图片,但是注意,这个ImageView从始至终都是没有显示在界面上的。

    Paste_Image.png

    这个ImageView并没有加载到布局。我们想直接调用正常View的生成图片方法,但是如果这样会生成图片失败。
    因为刚刚inflate的View是没有经过measure和layout的,没有大小,所有我们需要指定一下大小。

    所以我们调用layoutView方法指定大小

      /**
         * 手动测量摆放View
         * 对于手动 inflate 或者其他方式代码生成加载的View进行测量,避免该View无尺寸
         * @param v
         * @param width
         * @param height
         */
        public static void layoutView(View v, int width, int height) {
            // validate view.width and view.height
            v.layout(0, 0, width, height);
            int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
            int measuredHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
    
            // validate view.measurewidth and view.measureheight
            v.measure(measuredWidth, measuredHeight);
            v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
        }
    
    
        public static int px2dip(Context context, float pxValue) {
            final float scale = context.getResources().getDisplayMetrics().density;
            return (int) (pxValue / scale + 0.5f);
        }
    
    

    有了大小,就可以生成图片了。

    四、WebView、ScrollView、ListView和RecyclerView

    其实在开篇的工具类都已经介绍了,就不一一说了。直接把demo的代码一下就差不多了。

    四.1、WebView 生成图片

    @RuntimePermissions
    public class WebShotActivity extends BaseActivity {
        private WebView mWeb;
        private ImageView mIvResult;
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_web);
    
            mWeb = (WebView) findViewById(R.id.mWeb);
            mIvResult = (ImageView) findViewById(R.id.mIvResult);
    
    
            mWeb.setDrawingCacheEnabled(true);
    
    
            //支持javascript
            mWeb.getSettings().setJavaScriptEnabled(true);
            mWeb.getSettings().setUseWideViewPort(true);
            mWeb.getSettings().setLoadWithOverviewMode(true);
            //支持页面缩放
            //webView.getSettings().setBuiltInZoomControls(true);
            //提升渲染优先级
            //webView.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH);
            //不加载网络中的图片资源
            //webView.getSettings().setBlockNetworkImage(true);
            //HTML5 Cache
            //*mWeb.getSettings().setDomStorageEnabled(true);
            mWeb.getSettings().setAllowFileAccess(true);
            mWeb.getSettings().setAppCacheEnabled(true);
            //优先从本地cache中载入,其次才是从网络中载入,即使内容已经过期*//*
            mWeb.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
            mWeb.setWebViewClient(new WebViewClient() {
                @Override
                public boolean shouldOverrideUrlLoading(WebView view, String url) {
                    view.loadUrl(url);
                    return super.shouldOverrideUrlLoading(view, url);
                }
                @Override
                public void onPageStarted(WebView view, String url, Bitmap favicon) {
                    super.onPageStarted(view, url, favicon);
                }
                @Override
                public void onPageFinished(WebView view, String url) {
                    super.onPageFinished(view, url);
                }
                @Override
                public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
                    //Android TV中可以在这里返回true,按键交由onKeyDown方法处理
                    return super.shouldOverrideKeyEvent(view, event);
                }
                @Override
                public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                    super.onReceivedError(view, errorCode, description, failingUrl);
                }
            });
    
    
            mWeb.setWebChromeClient(new WebChromeClient() {
                @Override
                public void onProgressChanged(WebView view, int newProgress) {
                    super.onProgressChanged(view, newProgress);
    
                }
                @Override
                public void onReceivedTitle(WebView view, String title) {
                    super.onReceivedTitle(view, title);
                }
            });
    
            mWeb.loadUrl("https://m.baidu.com/?from=844b&vit=fps");
    
    
            findViewById(R.id.mTvShot).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    WebShotActivityPermissionsDispatcher.storageNeedWithCheck(WebShotActivity.this);
                }
            });
    
    
        }
    
        @NeedsPermission({Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
        void storageNeed() {
            int viewWidth = mWeb.getMeasuredWidth();
            int viewHeight = mWeb.getMeasuredHeight();
            if (viewWidth > 0 && viewHeight > 0) {
                Bitmap b = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.RGB_565);
                Canvas cvs = new Canvas(b);
                mWeb.draw(cvs);
                mIvResult.setImageBitmap(b);
                SimpleUtils.saveBitmapToSdCard(WebShotActivity.this,b,"styleWeb");
            }
    
        }
    
    
        @OnShowRationale({Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
        void storageRationale(final PermissionRequest request) {
            showRationaleDialog("存储权限是本程序必不可少的权限,请开启",request);
        }
    
        @OnPermissionDenied({Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
        void storageDenied() {
            openAppSetting("您拒绝了存储权限,请授权");
        }
    
        @OnNeverAskAgain({Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
        void storageAsk() {
            openAppSetting("您拒绝了存储权限,请授权");
        }
    }
    

    代码稍微长了一下,是因为做了存储权限检查和一些WebView的配置,我们该忽略的忽略,核心就是传入WebView生成图片那么一句而已。

    .
    .
    .

    四.2、ScrollView 生成图片

    Paste_Image.png
    public class StyleScrollView extends BaseActivity {
    
        private ImageView mIvRet;
        private ScrollView mScrollView;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_scroll);
    
            mIvRet = (ImageView) findViewById(R.id.mIvRet);
            mScrollView = (ScrollView) findViewById(R.id.mScrollView);
    
            findViewById(R.id.mTvShot).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Bitmap bitmap = SimpleUtils.shotScrollView(mScrollView);
                    mIvRet.setImageBitmap(bitmap);
                }
            });
        }
    }
    

    四.3、ListView 生成图片

    Paste_Image.png
    public class StyleLvActivity extends BaseActivity {
        private ListView mLv;
        private ImageView mIvRet;
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_lv);
            mLv = (ListView) findViewById(R.id.mLv);
            mLv.setAdapter(new MyAdapter());
            mIvRet = (ImageView) findViewById(R.id.mIvRet);
            findViewById(R.id.mTvShot).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Bitmap bitmap = SimpleUtils.shotListView(mLv);
                    mIvRet.setImageBitmap(bitmap);
                }
            });
        }
        
        class MyAdapter extends BaseAdapter{
            @Override
            public int getCount() {
                return 10;
            }
    
            @Override
            public Object getItem(int i) {
                return null;
            }
            @Override
            public long getItemId(int i) {
                return 0;
            }
            @Override
            public View getView(int i, View view, ViewGroup viewGroup) {
                // 简答粗暴了一些,只是为演示
                View  vi = getLayoutInflater().inflate(R.layout.item_lv,null);
                TextView textView = (TextView) vi.findViewById(R.id.mTv);
                textView.setText(i+"");
    
    
                return vi;
            }
        }
    }
    
    

    .
    .
    .

    四.4、RecyclerView 生成图片

    Paste_Image.png
    public class StyleRecyclerView extends BaseActivity {
        private RecyclerView mRecycler;
        private ImageView mIvRet;
        private List<DataBean> mDatas;
        private TestAdapter mAdapter;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_rv);
    
            initData();
            mRecycler = (RecyclerView) findViewById(R.id.mRecycler);
            mRecycler.setLayoutManager(new LinearLayoutManager(this));
            mRecycler.setAdapter(mAdapter = new TestAdapter());
    
            mIvRet = (ImageView) findViewById(R.id.mIvRet);
            findViewById(R.id.mTvShot).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Bitmap bitmap = SimpleUtils.shotRecyclerView(mRecycler);
                    mIvRet.setImageBitmap(bitmap);
                }
            });
        }
    
        private void initData()
        {
            mDatas = new ArrayList<DataBean>();
            DataBean dataBean = null;
            for (int i = 0; i < 6; i++)
            {
                dataBean = new DataBean();
                dataBean.title = "标题  "+i;
                dataBean.desc = "描述一下  "+i;
                mDatas.add(dataBean);
            }
        }
    
        class TestAdapter extends RecyclerView.Adapter<TestAdapter.MyViewHolder>{
            // 孩子数
            @Override
            public int getItemCount() {
                return mDatas.size();
            }
    
    
            // 创建视图
            @Override
            public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                MyViewHolder myViewHolder = new MyViewHolder(LayoutInflater.from(StyleRecyclerView.this)
                        .inflate(R.layout.item_rv,parent, false));
                return myViewHolder;
            }
    
            // 绑定视图视图  以前getView的事情  关键方法
            @Override
            public void onBindViewHolder(MyViewHolder holder, int position) {
                DataBean dataBean = mDatas.get(position);
                holder.mTvTitle.setText(dataBean.title);
                holder.mTvDesc.setText(dataBean.desc);
    
            }
    
            // 必须实现的Holder
            class MyViewHolder extends RecyclerView.ViewHolder
            {
                TextView mTvTitle;
                TextView mTvDesc;
    
                public MyViewHolder(View itemView) {
                    super(itemView);
                    mTvTitle = (TextView) itemView.findViewById(R.id.mTvTitle);
                    mTvDesc = (TextView) itemView.findViewById(R.id.mTvDesc);
                }
            }
    
        }
    }
    

    如上,这么一些类型就介绍完了。

    本篇完。

    GIT下载链接

    参考

    【Android】获取View的截图
    Android滚动截屏,ScrollView截屏,Listview截屏,Recyclerview截屏

    相关文章

      网友评论

      • 21fe789340c5:你Demo的 RecyclerView 里面的 LruCache 使用有问题,没重写 sizeOf ,默认返回 1,这样会你加了LruCache 跟没加一样,会OOM的

      本文标题:Android 根据View生成图片简易参考

      本文链接:https://www.haomeiwen.com/subject/djjrzttx.html