APP第十七次更新过程

本次更新的主要内容为增加分享功能,优化评论内容的显示方式,优化文章内容的显示方式等,下面分别介绍其过程。

一、分享功能的实现

这里的分享主要是文章的分享(虽然之前写的评论界面也是有分享按钮的,但是不论从开发者还是用户角度,我感觉评论的分享并没有什么用)。分享的途径大致可以分成两类:

第一种是注册腾讯的开发者账户,或者用友盟的分享sdk,但是他们都要按照一定的流程,调用他们定制的api接口,按照一定的标准才能分享,而且还要合规,比较繁琐。

第二种就是调用Android集成的分享接口,可以分享图片,链接,文件等,缺点就是界面比较凌乱,没有第三方api的整洁。

这里我采用的是第二种。

首先要确定的是,要分享的是什么,这里可以直接照搬微信分享的样式,比如下面这张图里面:

这个图片上,上面是文章标题,右下角是一个链接的图片,以为是新闻,所以还可以在标题下面写一段正文,所以我就把要分享的内容设定成这样:

之所以有个QR code,是因为我分享的只是图片,不能像微信sdk那样,可以点击图片直接看新闻,这里只能曲线救国,提供一个二维码,将文章的链接写入二维码,然后利用微信的提取二维码功能,就能看到新闻了,这里比微信sdk做法多了一步,不过还是能接受的。下面开始写代码。

 

在gradle里面,加入一行代码,引入zxing二维码库:

implementation 'cn.yipianfengye.android:zxing-library:2.2'

因为此时的文章浏览页面还没有任何的分享按钮,所以需要在Toolbar上面添加一个分享按钮。在res目录的menu子目录下面新建一个菜单文件article_panel.xml,这里面质只包含一个分享按钮:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item app:showAsAction="always"
        android:id="@+id/share_pic"
        android:icon="@drawable/ic_share"
        android:title="@string/share_article"/>
</menu>

因为这个时候,新闻页面是分成有图片和无图片的新闻来加载的,所以很多代码都要在两个Activity写成一样的,为了简洁一点,这里我添加一个两个新闻页面的共同基类ArticleBase类:

public class ArticleBase extends AppCompatActivity {

}

本来两个新闻页面都是派生自AppCompactActivity的,此时把他们的基类改成ArticleBase:

public class NewsDetail extends ArticleBase {
    ...
}



public class DetailWithPic extends ArticleBase {
    ...    
}

为了加载刚才创建的菜单,在ArticleBase里面重写onCreateOptionsMenu方法,这样就可以在两个文章页面都加载同样的菜单:

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        //将包含分享按钮的菜单映射到页面头部的Toolbar
        getMenuInflater().inflate(R.menu.article_panel,menu);
        return super.onCreateOptionsMenu(menu);
    }

然后再添加一个方法,将分享按钮被点击之后的执行的代码封装进去,供两个Activity调用,这个方法的参数分别就是我刚才设定的要分享的二维码,标题和正文:

    //分享的方法
    public void shareArticle(Bitmap sharePic,String articleTitle,String articleContent){
        ...
}

增加两个String全局变量,分别保存文章标题和部分正文,还有一个Bitmap,用来保存文章链接的二维码:

    //文章标题
    protected String shareTitle;
    //部分的正文
    protected String articleContent = "";
    //要分享的图片
    protected Bitmap shareRawBitmap;

打开两个Activity,初始化二维码:

        //初始化二维码
        shareRawBitmap = CodeUtils.createImage(articleUrl,110,110,BitmapFactory.decodeResource(getResources(),R.drawable.guanwang));

(没错,一行代码生成二维码,真是炒鸡方便的)

然后重写分享按钮被点击后的回调函数onOptionsItemSelected:

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        //判断是否是我们增加的分享按钮,其实此时ToolBar上面只有这一个按钮,所以这里的判断是多余的
        //但是不确定以后是否增加其他的按钮,所以先保留判断
        if (item.getItemId() == R.id.share_pic){
            if (shareRawBitmap != null){
                shareArticle(shareRawBitmap,shareTitle,articleContent);
            }else Log.d(TAG, "onOptionsItemSelected: 要分享的图片为空,请排查原因");
        }
        return super.onOptionsItemSelected(item);
    }

这里遇到了一个坑,那就是初始化二维码的时候,传入的中心的图片是xml格式的svg图片,但是svg图片解析后在这里是不能正常分辨的,所以这里得到的二维码没有中心的观网标志,这时候把解析的图片改成png或者jpg格式就可以正常解析了。

代码写到这里还只是铺垫,真正的难点在于如何在shareArticle方法内,把标题,正文,和二维码合成到一张图片上面,这里我使用了绘图相关的API方法,将所有的内容画到画布上,然后把画布上的图片分享出去。

shareArticle内部,首先新建一个Bitmap用作画布的底面,所有的内容都将画到这个Bitmap上面:

        //新建一张位图,用于分享
        Bitmap shareBitmap = Bitmap.createBitmap(410,180,Bitmap.Config.ARGB_8888);

然后是画布和画笔:

        //新建画布,参数为上面刚刚创建的位图,使用画布在位图上面创建要分享的内容
        Canvas shareCanvas = new Canvas(shareBitmap);
        //将要分享的位图缩放到理想的尺寸
        Bitmap sharingPic = Bitmap.createScaledBitmap(sharePic,110,110,false);
        //新建一个画笔,用于将要分享的内容画到画布上面
        Paint sharePaint = new Paint(Paint.ANTI_ALIAS_FLAG);

料都备齐了,下面是作画的过程,首先把底色涂成白色:

        //先绘制白色底色
        shareCanvas.drawColor(Color.rgb(0xff,0xff,0xff));

然后把二维码画到画布上:

        //首先将图片绘制到画布上面的合适位置
        shareCanvas.drawBitmap(sharingPic,(float) (shareBitmap.getWidth() - 108) , (float) (shareBitmap.getHeight() * 0.75 - 68) ,sharePaint);

然后是文字,把字体调到合适大小,直接写在画布合适位置:

    //将要分享的文章标题绘制到要分享的图片上面,首先应该设置分享文字的字体和颜色
        sharePaint.setTextSize(18f);
        //然后将文字绘制到画布,有5dp的Margin
        int titleLength = articleTitle.length();
        shareCanvas.drawText(titleLength >22 ? articleTitle.substring(0,22) : articleTitle,5,23,sharePaint);

标题下面是正文,字体要小一些,而且文章有很多行,所以需要在一个循环内,将文章分批次画到要分享的图片上:

        sharePaint.setTextSize(12f);
        //创建一个循环,将文字绘制到画布上面
        int contentLength = articleContent.length();
        //判断内容的行数
        int rowNum = contentLength / 25 > 6 ? 6 : contentLength / 25;
        for (int i = 0 ; i <= rowNum ; i ++){
            int clipEnd = (i + 1) * 25 >= contentLength ? contentLength: (i + 1) * 25;
            shareCanvas.drawText(articleContent.substring(i * 25,clipEnd),5,50 + i * 20,sharePaint);
        }

将此时已经合成的图片保存在本地(这一步可有可无):

        //下面的代码将文件保存到本地
        Uri shareLink = Uri.parse(MediaStore.Images.Media.insertImage(getContentResolver(),shareBitmap,null,null));
        String shareUrl = Environment.getExternalStorageDirectory() + "/guancha/";
        File guanchaUrl = new File(shareUrl);
        if (!guanchaUrl.exists()) guanchaUrl.mkdirs();
        String picUrl = shareUrl + String.format("%s.jpg",System.currentTimeMillis());
        picUrl = String.format(picUrl, System.currentTimeMillis());
        Log.d(TAG, "shareArticle: " + picUrl);
        File shareFileUrl = new File(picUrl);
        try {
            shareFileUrl.createNewFile();
            shareBitmap.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(shareFileUrl));
        } catch (IOException e) {
            e.printStackTrace();
        }

存储需要申请存储权限,但是权限申请已经在WelcomeActivity中申请,这里就不在复述了。

 

最后是调用Android系统的原生分享api,将合成的图片分享出去:

        //然后调用Android系统原生的分享接口,分享图片
        Intent intent = new Intent(Intent.ACTION_SEND);//action_send为分享的枚举类型
        //设置所分享内容的类型
        intent.setType("image/*");
        //将要分享的图片的uri传入intent
        intent.putExtra(Intent.EXTRA_STREAM,shareLink);
        Intent shareIntent = Intent.createChooser(intent,"分享文章到:");
        //启动分享的页面
        startActivity(shareIntent);

调试一下 :

在文件管理里面可以看到,分享的图片是这样的:

 

然后在微信聊天里长按图片就可以浏览新闻了:

二、优化评论显示方式

因为有关文章的内容,都是用爬虫解析出来的,所以文章也好,评论也好,总会有时候掺杂着一些html的元素,比如说下面这些:

上面两张图片里面的br标签对应于换行,strong标签对应于加粗,img标签对应于表情。

在这里可以借助Android原生的一个文本优化api来对html内容进行格式化,一句代码就可以了:

Html.fromHtml(comment);

这里出现了一个问题,这个只有一个参数的方法,在高版本的Android里面是一个过期的方法,所以要判断一下Android的版本,然后在高版本里面使用此方法的重载方法:

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return Html.fromHtml(comment,Html.FROM_HTML_MODE_COMPACT,source -> {
                ImageView htmlView = new ImageView(context);
                Picasso.get().load(source).placeholder(R.drawable.guanwang).into(htmlView);
                htmlDrawable = htmlView.getDrawable();
                if (htmlDrawable == null) Log.d(TAG, "formatComment: 图片为空");
                return htmlDrawable;
            },null);
        }else return Html.fromHtml(comment);

重载方法里面需要提供一个flag,然后需要提供一个ImageGetter参数,因为ImageGetter是一个函数式接口,所以这里用的一个lambda表达式生成一个ImageGetter的匿名实现类。

这里面最大的问题是,观网的表情是gif格式的图片,但是Android原生的view是不支持显示gif格式图片的,所以就导致Drawable.createFromStream方法如果传入是gif的流,则解析出错,并且返回null。

因此这里就绕了一个弯,用第三方框架Piccaso,先加载gif图片到一个缓冲ImageView里面,然后从缓冲的ImageView里面得到Drawable,就可以在ImageGetter的方法里面返回了。

调试一下:

可以看到,换行,加粗,表情等问题都解决了。

 

三、优化文章页面

这是最简单的一步,因为文章内容和评论内容是连成一体的,不太美观,所以我把文章内容和评论分别包含进一个CardView里面,设置一个间隔,然后给一个elevation,这样会有一些错落感。

在布局文件activity_detail_with_pic.xml和activity_news_detail.xml中,添加两个<CardView ... />标签,并且将内容和评论分别剪切进去:

        <androidx.cardview.widget.CardView
                android:layout_width="match_parent"
                android:background="#ffffffff"
                android:elevation="2dp"
                app:cardCornerRadius="0dp"
                android:layout_height="wrap_content">
                <LinearLayout
                    android:layout_width="match_parent"
                    android:orientation="vertical"
                    android:animateLayoutChanges="true"
                    android:id="@+id/detail_no_pic_frame"
                    android:layout_height="wrap_content">
                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:id="@+id/news_detail_text"
                        android:textSize="15sp"
                        />
                </LinearLayout>
            </androidx.cardview.widget.CardView>
            <androidx.cardview.widget.CardView
                android:layout_width="match_parent"
                android:background="#ffffff"
                android:elevation="2dp"
                android:layout_marginTop="6dp"
                app:cardCornerRadius="0dp"
                android:layout_height="wrap_content">
                <androidx.recyclerview.widget.RecyclerView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/no_pic_recycler"
                    />
                <View
                    android:layout_width="match_parent"
                    android:layout_height="60dp"/>
            </androidx.cardview.widget.CardView>

调试一下,就能看到错落的效果了:

 

 

本次更新结束。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值