控件(1)-使用URL创建的Sprite

作者: qufl | 来源:发表于2017-01-05 01:17 被阅读136次

    你可以使用图片名创建一个Sprite,可以使用预加载好的纹理创建一个Sprite,但是引擎没有提供一个使用url来创建Sprite的方法。

    在游戏中每次遇到需要从服务端获取一个玩家的头像url,然后通过这个url获取玩家头像并显示的时候,我就会想,如果能直接用url创建Sprite该多好。
    需要显示配置在服务端的推广图片时。。。
    需要显示配置在服务端的商品列表时。。。


    其实很早就想写这样一个控件了,只是一直想在其之上附加各种功能(预加载? 默认纹理? 使用本地缓存?等),导致自己一直想不明白要做什么,也就是需求不明确。今天终于沉下心来实现了一下这个URLSprite。

    需求

    1.使用url创建Sprite
    2.可以设置默认texture
    3.下载好的资源保存在本地,并且可以指定文件名
    4.可以使用已下载好的本地资源

    大致思路

    1.URLSprite继承自Sprite
    2.使用一个Texture2D指针保存默认纹理
    3.使用cocos2d引擎提供的HttpClient来下载图片
    4.使用FILE将图片保存在本地
    5.使用cocos2d引擎的FileUtils来查找本地资源


    URLSprite

    先来看看URLSprite的头文件:

    //
    //  QFLURLSprite.hpp
    //  MyTemplet
    //
    //  Created by QuFangliu on 2017/1/4.
    //  Copyright © 2017年 qufangliu. All rights reserved.
    //
    //  使用URL创建一个Sprite,可以设置默认texture,对象可以自动使用HttpClient来下载图片资源
    
    #ifndef QFLURLSprite_hpp
    #define QFLURLSprite_hpp
    
    #include <stdio.h>
    #include "cocos2d.h"
    #include "network/HttpClient.h"
    
    /*
     QFLURLSprite的状态
     */
    enum QFLURLSprite_State{
        URLSprite_State_NoTexture = 0,  //无纹理
        URLSprite_State_Default = 1,    //默认纹理
        URLSprite_State_Loaded = 2,     //下载好的纹理(或者使用指定的本地文件)
    };
    
    /*
     URLSprite
     */
    class QFLURLSprite : public cocos2d::Sprite
    {
    CC_CONSTRUCTOR_ACCESS:
        QFLURLSprite();
        virtual ~QFLURLSprite();
        
    public:
        
        /*
            Create
         */
        static QFLURLSprite* create();
        static QFLURLSprite* create(const std::string &strURL);
        //指定(已)下载好的文件名,并且选择是否直接使用本地资源
        static QFLURLSprite* create(const std::string &strURL, const std::string &strFileName, bool bForceRefresh);
        
        /*
            Init
         */
        virtual bool init();
        virtual bool initWithURL(const std::string &strURL);
        virtual bool initWithConfig(const std::string &strURL, const std::string &strFileName, bool bForceRefresh);
        
        /*
            Refresh
         */
        void refresh(bool bShowDefault = false);
        
        void loadImage();
        void loadImageCallback(cocos2d::network::HttpClient* pClient, cocos2d::network::HttpResponse* pResponse);
        
        /*
            Default Texture
         */
        void setDefaultTexture(cocos2d::Texture2D* pTexture);
        cocos2d::Texture2D* getDefaultTexture() { return m_pDefaultTexture; }
        
        /*
            Config
         */
        CC_SYNTHESIZE(std::string, m_strURL, SpriteURL);        //图片的下载地址
        CC_SYNTHESIZE(std::string, m_strName, FileName);        //下载好后或者指定的文件名
        CC_SYNTHESIZE(bool, m_bForceRefresh, ForceRefresh);     //是否强制刷新,false则找到本地文件就不刷新
        
    private:
        /*
            tools
         */
        std::string getFileNameByURL(const std::string &strURL);
        std::string checkNativeFile(const std::string &strName);
        
        cocos2d::Texture2D* m_pDefaultTexture;  //默认纹理
        QFLURLSprite_State m_eState;            //状态
    };
    
    #endif /* QFLURLSprite_hpp */
    

    创建方式模仿了Cocos2d引擎的Sprite的二段构建方法,即重载不同的create方法,在create中去找对应的init方法,真正进行Sprite的初始化。

    然后使用了一个枚举来标志URLSprite的状态,在setDefaultTexture时,如果当前不是使用的url指定的图片,则显示默认纹理。

    另外还提供了refresh方法,以供控件的重用。

    上面代码比较简单,基本都有说明了,这里就不赘述了。

    下面把cpp文件也粘贴出来,懒得去看Github的童鞋可以用这个,但不保证此处是最新版本。

    //
    //  QFLURLSprite.cpp
    //  MyTemplet
    //
    //  Created by QuFangliu on 2017/1/4.
    //  Copyright © 2017年 qufangliu. All rights reserved.
    //
    
    #include "QFLURLSprite.hpp"
    
    USING_NS_CC;
    using namespace network;
    
    QFLURLSprite::QFLURLSprite()
    {
        m_strURL = "";
        m_strName = "";
        m_bForceRefresh = true;
        m_pDefaultTexture = nullptr;
        m_eState = QFLURLSprite_State::URLSprite_State_NoTexture;
    }
    
    QFLURLSprite::~QFLURLSprite()
    {
        
    }
    
    QFLURLSprite* QFLURLSprite::create()
    {
        QFLURLSprite* pSprite = new (std::nothrow) QFLURLSprite();
        
        if (pSprite && pSprite->init()) {
            pSprite->autorelease();
            return pSprite;
        }
        else {
            CC_SAFE_DELETE(pSprite);
            return nullptr;
        }
    }
    
    QFLURLSprite* QFLURLSprite::create(const std::string &strURL)
    {
        QFLURLSprite* pSprite = new (std::nothrow) QFLURLSprite();
        
        if (pSprite && pSprite->initWithURL(strURL)) {
            pSprite->autorelease();
            return pSprite;
        }
        else {
            CC_SAFE_DELETE(pSprite);
            return nullptr;
        }
    }
    
    QFLURLSprite* QFLURLSprite::create(const std::string &strURL, const std::string &strFileName, bool bForceRefresh)
    {
        QFLURLSprite* pSprite = new (std::nothrow) QFLURLSprite();
        
        if (pSprite && pSprite->initWithConfig(strURL, strFileName, bForceRefresh)) {
            pSprite->autorelease();
            return pSprite;
        }
        else {
            CC_SAFE_DELETE(pSprite);
            return nullptr;
        }
    }
    
    bool QFLURLSprite::init()
    {
        if (Sprite::init()) {
            return true;
        }
        else {
            return false;
        }
    }
    
    bool QFLURLSprite::initWithURL(const std::string &strURL)
    {
        if (Sprite::init()) {
            m_strURL = strURL;
            m_strName = this->getFileNameByURL(m_strURL);
            
            //下载图片
            this->refresh();
            return true;
        }
        else {
            return false;
        }
    }
    
    bool QFLURLSprite::initWithConfig(const std::string &strURL, const std::string &strFileName, bool bForceRefresh)
    {
        if (Sprite::init()) {
            m_strURL = strURL;
            m_strName = strFileName;
            m_bForceRefresh = bForceRefresh;
            
            if (m_bForceRefresh) {
                this->refresh();
            }
            else {
                //查找纹理
                auto pTexture = Director::getInstance()->getTextureCache()->getTextureForKey(strFileName);
                if (pTexture) {
                    m_eState = QFLURLSprite_State::URLSprite_State_Loaded;
                    this->initWithTexture(pTexture);
                }
                else {
                    //查找本地文件
                    std::string strNative = this->checkNativeFile(m_strName);
                    if (strNative != "") {
                        m_eState = QFLURLSprite_State::URLSprite_State_Loaded;
                        this->initWithFile(strNative);
                    }
                    else {
                        //下载图片
                        this->refresh();
                    }
                }
            }
            return true;
        }
        else {
            return false;
        }
    }
    
    //下载图片的逻辑
    void QFLURLSprite::refresh(bool bShowDefault)
    {
        if (bShowDefault && m_pDefaultTexture) {
            m_eState = QFLURLSprite_State::URLSprite_State_Default;
            this->setTexture(m_pDefaultTexture);
        }
        else {}
        
        if (m_strURL.empty()) {
            CCLOG("Invalid url!");
            return;
        }
        else {}
        
        if (m_strName.empty()) {
            m_strName = this->getFileNameByURL(m_strURL);
        }
        else {}
        
        this->loadImage();
    }
    
    void QFLURLSprite::loadImage()
    {
        HttpRequest* pRequest = new (std::nothrow) HttpRequest();
        if (pRequest) {
            pRequest->setUrl(m_strURL.c_str());
            pRequest->setRequestType(cocos2d::network::HttpRequest::Type::GET);
            pRequest->setTag("LoadImage");
            pRequest->setResponseCallback(CC_CALLBACK_2(QFLURLSprite::loadImageCallback, this));
            
            HttpClient::getInstance()->send(pRequest);
            
            pRequest->release();
            
            //防止被删除
            CC_SAFE_RETAIN(this);
        }
        else {
            CC_SAFE_DELETE(pRequest);
            CCLOG("Can not create HttpRequest!");
        }
    }
    
    void QFLURLSprite::loadImageCallback(cocos2d::network::HttpClient *pClient, cocos2d::network::HttpResponse *pResponse)
    {
        //和load对应
        CC_SAFE_RELEASE(this);
        
        if (this->getReferenceCount() == 0) {
            return;
        }
        else {}
        
        if (pResponse) {
            if (pResponse->isSucceed()) {
                //保存请求的图片数据
                std::vector<char>* pBuffer = pResponse->getResponseData();
                std::string strBuffer = std::string(pBuffer->begin(), pBuffer->end());
                std::string strPath = FileUtils::getInstance()->getWritablePath() + m_strName;
                
                //图片保存到本地
                FILE *pFile = fopen(strPath.c_str(), "wb+");
                fwrite(strBuffer.c_str(), 1, strBuffer.size(), pFile);
                fclose(pFile);
                
                //使用下载的图片
                m_eState = QFLURLSprite_State::URLSprite_State_Loaded;
                this->setTexture(strPath);
            }
            else {
                CCLOG("Request error:%s", pResponse->getErrorBuffer());
            }
        }
        else {
            CCLOG("Request failed!");
        }
    }
    
    void QFLURLSprite::setDefaultTexture(cocos2d::Texture2D *pTexture)
    {
        m_pDefaultTexture = pTexture;
        
        if (m_eState != QFLURLSprite_State::URLSprite_State_Loaded) {
            m_eState = QFLURLSprite_State::URLSprite_State_Default;
            this->initWithTexture(m_pDefaultTexture);
        }
        else {}
    }
    
    std::string QFLURLSprite::getFileNameByURL(const std::string &strURL)
    {
        return strURL.substr(strURL.find_last_of("/") + 1);
    }
    
    std::string QFLURLSprite::checkNativeFile(const std::string &strName)
    {
        std::string strPath = "";
        
        //查找项目资源
        strPath = FileUtils::getInstance()->fullPathForFilename(strName);
        if (FileUtils::getInstance()->isFileExist(strPath)) {
            return strPath;
        }
        else {}
        
        //查找读写路径下的资源
        strPath = FileUtils::getInstance()->getWritablePath() + strName;
        if (FileUtils::getInstance()->isFileExist(strPath)) {
            return strPath;
        }
        else {}
        
        return "";
    }
    
    

    代码比较简单,也没有周详的去想每一个细节。例如如果url指向的不是一个可用的图片,而是一个mp3文件,要如何处理?写文件很慢要不要另外启动一个异步线程来做?
    这些需要的时候再优化啦~


    如果小伙伴们发现有问题,或者有更好的实现方式,或者其他建议,欢迎联系噢~

    下一个UI就做个道具列表吧。

    相关文章

      网友评论

      • qufl:我是猪。。

      本文标题:控件(1)-使用URL创建的Sprite

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