美文网首页Android技术知识Android开发经验谈
Android自定义原生分享界面以及View转Bitmap的问题

Android自定义原生分享界面以及View转Bitmap的问题

作者: 那些云 | 来源:发表于2018-01-20 11:48 被阅读3055次

    前言

    前几天设计师说要把APP里的一个界面转成图片分享出去,本来想随意写个Dialog算了,不过看到知乎里面的分享界面觉得还不错,反正无聊自己也写成这样算了,现在记录下。

    效果图: Screenshot_20180120-114048.jpg

    1、界面

    据观察知乎的分享界面就是用BottomSheetDialog或者BottomSheetDialogFragment写的,所以这里我就用BottomSheetDialogFragment,具体界面就是

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/share_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="@dimen/dip_fifty_six"
            android:layout_marginStart="@dimen/dip_sixteen"
            android:gravity="center_vertical"
            android:text="@string/share_out_to"
            android:textColor="@color/text_black_474c59"
            android:textSize="@dimen/sp_sixteen"/>
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/share_recycler_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    
    </LinearLayout>
    

    RecyclerView的LayoutManager就用GridLayoutManager,设置一行三个填充。

    2、数据

    现在来获取手机里具有分享功能的APP,在我们的手机中安装了的应用支持ACTION_SEND的Activity都会被列入可选列表。

    由于我们需要显示的内容为APP的Icon、Label就可以了,所以通过PackageManager来获取ResolveInfo列表就行。具体代码为:

    List<ResolveInfo> resolveList = packageManager.queryIntentActivities(intent, 0);
    

    由于公司项目已经集成了微信SDK,而且由原生分享出去的图片在微信里并不会显示来自哪个APP,所以在获取到resolveList后我需要对获取到的列表进行筛选处理,去掉分享到微信的原生分享Activity数据,这里直接通过微信的包名鉴别,去掉即可。然后再单独在ResolveInfo集合的前面插入自己的数据,这样还能确保分享到朋友和分享到朋友圈始终在显示的最前面两个。

        /**
         * 得到支持分享的应用
         *
         * @return 返回支持分享的app集合
         */
        public List<ShareItem> scanShreaApp() {
            mShareIntent = new Intent(Intent.ACTION_SEND);
            mShareIntent.setType("image/*");
            PackageManager packageManager = mContext.getPackageManager();
            mResolveInfos.clear();
            for (ResolveInfo resolveInfo : packageManager.queryIntentActivities(mShareIntent, 0)) {
                if (!resolveInfo.activityInfo.packageName.contains("com.tencent.mm")) {
                    mResolveInfos.add(resolveInfo);
                }
            }
            ArrayList<ShareItem> shareItems = new ArrayList<>();
            for (ResolveInfo resolveInfo : mResolveInfos) {
                ShareItem shareItem = new ShareItem(resolveInfo.loadLabel(packageManager),
                        resolveInfo.loadIcon(packageManager));
                shareItems.add(shareItem);
            }
            return shareItems;
        }
    

    然后在使用时将分享给朋友和分享到朋友圈加进去:

    shareItems.add(new ShareItem("发送给朋友",
                    ContextCompat.getDrawable(getContext(), R.drawable.share_icon_wechat)));
    shareItems.add(new ShareItem("发送到朋友圈",
                    ContextCompat.getDrawable(getContext(), R.drawable.share_icon_moments)));
    shareItems.addAll(mShare.scanShreaApp());
    mShareItemAdapter.setShareItems(shareItems);
    

    做点击事件时单独区分调用微信分享还是原生分享

    mShareItemAdapter.setItmClickListener(new ShareItemAdapter.OnShareItmClickListener() {
                @Override
                public void onClick(int position) {
                    mViewBitmap = generateShareImg(time, mArticleTitle, mArticleContent);
                    mShareUri = BitmapUtil.saveBitmap(mViewBitmap);
                    if (position == 0) {
                        shareToWX(SendMessageToWX.Req.WXSceneSession);
                    } else if (position == 1) {
                        shareToWX(SendMessageToWX.Req.WXSceneTimeline);
                    } else {
                        mShare.share(position - 2, mShareUri);
                    }
                    dismiss();
                }
            });
    

    3、View转Bitmap

    现在来说下View转Bitmap遇到的问题,这个东西还是可以用到很多地方的,比如之前写弹底部弹窗,设计师要求弹窗显示的时候,背景是当前界面的高斯模糊后的效果,在Android里面写高斯模糊可没有iOS那么方便,人家直接就是系统提供的一个控件,咱们就得把当前界面截屏然后转为Bitmap,然后再给它高斯模糊下,再作为背景。

    其实一般View转Bitmap就两个套路,

    套路一

    Bitmap shareBitmap = Bitmap.createBitmap(view.getMeasuredWidth(),
                    view.getMeasuredHeight(),
                    Bitmap.Config.ARGB_8888);
            Canvas c = new Canvas(shareBitmap);
            view.draw(c);
            
    

    或者

    套路二

    view.setDrawingCacheEnabled(true);
    Bitmap shareBitmap = Bitmap.createBitmap(view.getDrawingCache());
    view.setDrawingCacheEnabled(false);
    

    在使用中,如果view是已经显示在界面上了,那么直接使用时没什么问题的,但如果是一个未曾显示的界面,想要转换成Bitmap就需要先创建并计算大小,代码如下:

    View view = View.inflate(this, R.layout.share_out_layout, null);
    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.setDrawingCacheEnabled(true);
    Bitmap shareBitmap = Bitmap.createBitmap(view.getDrawingCache());
    view.setDrawingCacheEnabled(false);
    return shareBitmap;
    

    到这里其实已经差不多了,但是!!! 我在Activity中这样使用一切正常,放在Fragment中用的时候就GG了,报错信息:java.lang.NullPointerException: Attempt to invoke virtual method 'int android.graphics.Bitmap.getWidth()' on a null object reference 查看Google搜索出来的结果,有人说测量view的时候,如果你的布局中包含有 RelativeLayout )API 为17 或者 低于17 会报空指针异常。
    但是我去掉RelativeLayout后还是有问题,再把measure时的参数改成int + View.MeasureSpec.EXACTLY ,就确实不会报错了,但是由于view的大小被设置为固定数值,效果太差了。后面试了下套路一,一切正常!!!

    所以文章到此结束.....

    相关文章

      网友评论

        本文标题:Android自定义原生分享界面以及View转Bitmap的问题

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