动手撸一个金馆长表情库——爬虫及异步请求

前言:对于一些社交达人,微信或者QQ斗图几乎家常便饭。然而许多人手机里却只有那么几个表情,这样怎么在表情大战中取胜呢?不过不要忘了,我们是程序猿,没有弹药库自己造呗!于是就有了这篇文章。

项目github地址:https://github.com/VinceBarry/BiaoQingLib


功能展示

先看看最终效果(重点在功能,界面我就不作美化了,有兴趣的自己做一些美化)

pic1

pic2

这个app一共包含700+张金馆长表情,当我点击某张图片时,能够将图片发送到微信,QQ或其他第三方平台上;当长按图片时,能够将图片保存到特定的文件夹中;点击底部按钮时能够加载更多表情。这里只是功能实现讲解,如果学会了以后要多少弹药只是加几行代码的事。心动了吗?下面开始吧。

项目依赖

compile files('libs/jsoup-1.9.2.jar')
compile 'io.reactivex:rxjava:1.0.14'
compile 'io.reactivex:rxandroid:1.0.1'
compile 'com.squareup.picasso:picasso:2.5.2'

该项目需要用到上面几个库,分别是网络爬虫Jsoup,异步请求RxJava,RxAndroid和图片请求Picasso。对于每一个库的用法,RxJava可以翻阅我的上一篇博文,Picasso的用法我会在文中说明,相对简单;Jsoup参阅:http://www.open-open.com/jsoup/, 写的很详细。这几个库是该项目的基础,一旦会用就可以开始撸了。

具体实现

1.项目结构

如图所示,由于项目较简单,我就没有分包了:

pic3

2.界面布局

本项目只有一个Activity,我们只要在布局文件中添加一个GridView和底部一个Button即可。然后在Activity中实例化,这里就不多说了。

3.数据获取

表情的数据来源于 http://qq.yh31.com/zjbq/0551964.html ,由于我们需要使用爬虫爬取表情的图片地址,所以先查看网页的源码,发现我们需要的表情图片地址为:
<img src="/tp/zjbq/201606010944172985.jpg" alt="" />
于是就很明确了,我们先把img标签的部分过滤出来,但是img标签还有一部分图片不是我们所需的表情,如:<img class="pic2" src="/tp/zjbq/201604041253139124.gif" width="150" height="150" alt="屌丝猴qq表情,关于屌丝猴的图片大全" border="0" />。经过观察,发现不是我们所需的表情的img标签的class都为pic2,这样问题就解决了:

try {
                                Document doc = Jsoup.connect(URL)
                                        .timeout(3000)
                                        .get();
                                Elements elements = doc.getElementsByTag("img");
                                for (Element e : elements) {
                                    if (!e.attr("class").toString().equals("pic2")) {
                                        //TODO:
                                    }
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }

上面的代码便是网络爬虫的部分。先过滤出img标签的内容,然后二次筛选,筛去class为“pic2”的部分。上面代码中的TODO部分便可以对我们已经筛选的表情的Element进行处理,到此我们数据抓取成功。

4.异步请求

获取到的表情图片的地址是String类型,下面我们通过RxJava来实现异步请求并将数据加载(这部分如果不熟悉RxJava的童鞋也可以用Handler或者AsyncTask等等异步请求,这里不多说了)
首先创建事件源(下面的步骤跟上一篇文章类似),我把代码贴出来:

public class RxBiaoQing {
    private final static String URLPrefix = "http://qq.yh31.com";

    public static Observable<Bitmap> getBiaoQing(final Context context, final String URL) {
        return Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                            try {
                                Document doc = Jsoup.connect(URL)
                                        .timeout(3000)
                                        .get();
                                Elements elements = doc.getElementsByTag("img");
                                for (Element e : elements) {
                                    if (!e.attr("class").toString().equals("pic2")) {
                                        subscriber.onNext(URLPrefix + e.attr("src").toString());
//                            L.i(URLPrefix+e.attr("src").toString());
                                    }
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }

            }
        }).flatMap(new Func1<String, Observable<Bitmap>>() {
            @Override
            public Observable<Bitmap> call(String s) {
                Bitmap bitmap = null;
                try {
                    bitmap = Picasso.with(context).load(s).get();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return Observable.just(bitmap);
            }
        }).subscribeOn(Schedulers.io())
          .observeOn(AndroidSchedulers.mainThread());
    }

}

在事件源中进行网络表情爬取,每获得一个所需表情的图片地址时将其处理为Bitmap(这里使用Picasso库将String转化为Bitmap),注意flatmap将String变换为Bitmap的过程和调度器将事件源和观察者异步线程的过程。事件源完成后便是观察者的完成了。下面代码:

final Observer<Bitmap> observer = new Observer<Bitmap>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(Bitmap bitmap) {
                lruCache.set(position+"",bitmap);
                L.i("size",lruCache.size()+"");
                L.i("size","max:"+lruCache.size()+"");
                L.i("size",position+"");
                position++;
                biaoQingAdapter.notifyDataSetChanged();
                L.i(bitmap.toString());
            }
        };

上面便是观察者在接收到事件源发出的通知(即Bitmap对象)后将Bitmap写入LruCache缓存并通知GridView显示的过程。
LruCache缓存我使用的是Picasso库中的,也可以自己动手使用原生的LruCache,但本人感觉原生的不如Picasso方便。然后将事件源与观察者绑定:RxBiaoQing.getBiaoQing(MainActivity.this,URL).subscribe(observer);

上面完成了网络图片的异步请求和图片缓存的过程,下面便是将图片显示在GridView上。

5.数据展示

这一步最主要的是配置adapter。继承BaseAdapter,代码如下:

public class BiaoQingAdapter extends BaseAdapter {
    private LayoutInflater layoutInflater;
    private Context context;
    private LruCache lruCache;

    public BiaoQingAdapter(LruCache lruCache, Context context) {
        layoutInflater = LayoutInflater.from(context);
        this.lruCache = lruCache;
        this.context = context;
    }

    @Override
    public int getCount() {
        return lruCache.size();
    }

    @Override
    public Object getItem(int position) {
        return lruCache.get(position+"");
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder = null;
        if(convertView == null){
            viewHolder = new ViewHolder();
            convertView = layoutInflater.inflate(R.layout.item_gridview,null);
            viewHolder.imageView = (ImageView) convertView.findViewById(R.id.item_iv);
            convertView.setTag(viewHolder);
        }else{
            viewHolder = (ViewHolder) convertView.getTag();
        }
        Bitmap bitmap = lruCache.get(position+"");
        viewHolder.imageView.setImageBitmap(bitmap);
        return convertView;
    }
    class ViewHolder{
        ImageView imageView;
    }
}

BaseAdapter的复写我就不多说了,主要要注意的是我将LruCache缓存传入适配器中,然后通过get()方法找寻到position对应的Bitmap,将Bitmap在对应的GridView的位置中显示出来。

6.图片下载分享

保存这个部分我就直接贴代码了。。。

public void saveToSD(int position){
        String fileName = "biaoqing"+position +".jpg";
        File appDir = new File(Environment.getExternalStorageDirectory(),"Tencent/MicroMsg/WeiXin");
        if(!appDir.exists()){
            Toast.makeText(context,"您未安装微信`.`!",Toast.LENGTH_SHORT).show();
            appDir.mkdirs();
        }
        File file = new File(appDir,fileName);
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            lruCache.get(position+"").compress(Bitmap.CompressFormat.JPEG,100,fileOutputStream);
            fileOutputStream.flush();
            fileOutputStream.close();
            Toast.makeText(context,"弹药已经装到库中!",Toast.LENGTH_SHORT).show();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

注意上面的文件夹路径在SD根目录下的Tencent/MicroMsg/WeiXin中,你可以自己修改切换。此外分享部分就不多说了,可以通过直接调用微信SDK或mob的ShareSDK来实现发送。当然了,intent也可以试试。

7.添加权限

辛苦了半天还是不行?看看有没有添加权限吧~~博主马大哈总是忘记这个地方。。。还有Android6.0的权限问题也要注意喔!

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

总结

这个项目写下来还是很有价值的第一,你获得了一个无穷的弹药库,以后斗图不再低人一等啦第二,你将收获RxJava,Picasso,Jsoup甚至BaseAdapter的使用技巧,这些都是很有价值的。。。由于博主水平不够,可能存在一些疏漏,欢迎评论指出。另外项目github地址在最上面,最好结合源码阅读此文。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值