大连游戏行业不是太发达,最后选择在一家应用外包公司工作,在工作和业余学习过程中积累了一点微不住道的经验,希望分享给热爱游戏的小伙伴们。
在应用开发过程中会常常有用户上传头像的功能,在网上找了N多资料发现没有人具体介绍过该用cocos2d-x实现。这篇文章就来介绍一下如何在Android和IOS平台上实现该功能。
欢迎热爱游戏开发的小伙伴骚扰:
电邮:[email protected]
企鹅:601322806
先传一张完成后的图片一饱眼福:
直接上代码:
头文件 ImagePicker.h
/************************************************************************** * Copyright (c) 2015, pxbomb, All rights reserved. * File : ImagePicker.h * Date : 2015/06/02 18:02 * Author : 田伟汉 * Email : [email protected] * Depict : 图像选择器 **************************************************************************/ #ifndef _IMAGEPICKER_H_ #define _IMAGEPICKER_H_ #include "cocos2d.h" #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) #include "platform/android/jni/JniHelper.h" #include <jni.h> #endif // CC_PLATFORM_ANDROID /** * 图像选择器 */ class ImagePicker { public: // 获取选择器单例 static ImagePicker* getInstance(); // 销毁 static void destoryInstance(); public: // 显示本地相册与相机选择器 void callImagePickerWithPhotoAndCamera(const std::function<void(std::string)>& callback); // 设置监听 void setListener(const std::function<void(std::string)>& callback); // 移除监听 void removeListener(); // 打开相册 void openPhoto(); // 打开相机 void openCamera(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) // 设置AppController void setViewController(void* viewController); #endif // CC_PLATFORM_IOS protected: // 初始化 bool init(); ImagePicker(); protected: std::function<void(std::string)> m_callback; static ImagePicker* s_instance; #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) void* m_viewController; #endif // CC_PLATFORM_IOS }; #endif // _IMAGEPICKER_H_
实现文件 ImagePicker.cpp
#include "ImagePicker.h" //-------------------------------------------------- #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #import "ImagePickerViewController.h" #import "RootViewController.h" #endif //-------------------------------------------------- #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) #define JAVA_CLASS "org/cocos2dx/cpp/ImagePicker" #define JAVA_FUNC_OPEN_PHOTO "openPhoto" #define JAVA_FUNC_OPEN_CAMERA "openCamera" #endif //-------------------------------------------------- USING_NS_CC; //-------------------------------------------------- ImagePicker* ImagePicker::s_instance = NULL; //-------------------------------------------------- ImagePicker* ImagePicker::getInstance() { if (s_instance == NULL) { s_instance = new ImagePicker(); } return s_instance; } //-------------------------------------------------- void ImagePicker::destoryInstance() { CC_SAFE_DELETE(s_instance); } //-------------------------------------------------- ImagePicker::ImagePicker() :m_callback(nullptr) { Director::getInstance()->getEventDispatcher()->addCustomEventListener("ImagePickerEvent", [=](EventCustom* eve) { std::string* path = (std::string*)eve->getUserData(); if (path && m_callback != nullptr) { m_callback(*path); } }); } //-------------------------------------------------- void ImagePicker::callImagePickerWithPhotoAndCamera(const std::function<void(std::string)>& callback) { s_instance->init(); setListener(callback); } //-------------------------------------------------- void ImagePicker::setListener(const std::function<void(std::string)>& callback) { m_callback = callback; } //-------------------------------------------------- void ImagePicker::removeListener() { m_callback = nullptr; } //-------------------------------------------------- void ImagePicker::openPhoto() { #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) ImagePickerViewController* imagePickerViewController = [[ImagePickerViewController alloc] initWithNibName:nil bundle:nil]; RootViewController* _viewController = (RootViewController*)m_viewController; [_viewController.view addSubview:imagePickerViewController.view]; [imagePickerViewController localPhoto]; #endif #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) JniMethodInfo info; bool ret = JniHelper::getStaticMethodInfo(info, JAVA_CLASS, JAVA_FUNC_OPEN_PHOTO,"()V"); if (ret) { info.env->CallStaticVoidMethod(info.classID, info.methodID); } #endif } //-------------------------------------------------- void ImagePicker::openCamera() { #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) ImagePickerViewController* imagePickerViewController = [[ImagePickerViewController alloc] initWithNibName:nil bundle:nil]; RootViewController* _viewController = (RootViewController*)m_viewController; [_viewController.view addSubview:imagePickerViewController.view]; [imagePickerViewController takePhoto]; #endif #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) JniMethodInfo info; bool ret = JniHelper::getStaticMethodInfo(info, JAVA_CLASS, JAVA_FUNC_OPEN_CAMERA,"()V"); if (ret) { info.env->CallStaticVoidMethod(info.classID, info.methodID); } #endif } //-------------------------------------------------- bool ImagePicker::init() { cocos2d::Size visibleSize = Director::getInstance()->getVisibleSize(); //------------------------------------- // 根层 //------------------------------------- LayerColor* m_layer = LayerColor::create(Color4B(0, 0, 0, 125)); m_layer->retain(); //------------------------------------- // 按钮背景 //------------------------------------- Sprite* sprite = Sprite::create("ImagePicker/bk.png"); sprite->setAnchorPoint(Vec2(0.5, 0)); sprite->setPosition(Vec2(visibleSize.width/2, 0)); m_layer->addChild(sprite); //------------------------------------- // 按钮 //------------------------------------- Menu* menu = Menu::create(); menu->setPosition(Vec2::ZERO); m_layer->addChild(menu); //------------------------------------- MenuItemImage* btnPhoto = MenuItemImage::create("ImagePicker/ButtonPhoto.png", "ImagePicker/ButtonPhoto1.png", [=](Ref* p) { openPhoto(); }); btnPhoto->setAnchorPoint(Vec2(0.5, 1)); btnPhoto->setPosition(Vec2(visibleSize.width / 2, 280)); menu->addChild(btnPhoto); //------------------------------------- MenuItemImage* btnCamera = MenuItemImage::create("ImagePicker/ButtonCamera.png", "ImagePicker/ButtonCamera1.png", [=](Ref* p) { openCamera(); }); btnCamera->setAnchorPoint(Vec2(0.5, 1)); btnCamera->setPosition(btnPhoto->getPosition() + Vec2(0, -btnPhoto->getContentSize().height)); menu->addChild(btnCamera); //------------------------------------- MenuItemImage* btnCancel = MenuItemImage::create("ImagePicker/ButtonCancel.png", "ImagePicker/ButtonCancel1.png", [=](Ref* p) { float height = sprite->getContentSize().height; MoveBy* move = MoveBy::create(0.2, Vec2(0, -height)); sprite->runAction(move); menu ->runAction(move->clone()); Sequence* seq = Sequence::createWithTwoActions(FadeOut::create(0.2), RemoveSelf::create()); m_layer->runAction(seq); }); btnCancel->setAnchorPoint(Vec2(0.5, 1)); btnCancel->setPosition(btnCamera->getPosition() + Vec2(0, -btnCamera->getContentSize().height - 20)); menu->addChild(btnCancel); //------------------------------------- // 文字 //------------------------------------- Label* textPhoto = Label::createWithSystemFont("Photo", "", 24); textPhoto->setPosition(btnPhoto->getContentSize() / 2); textPhoto->setTextColor(Color4B::BLACK); btnPhoto->addChild(textPhoto); //------------------------------------- Label* textCamera = Label::createWithSystemFont("Camera", "", 24); textCamera->setPosition(btnPhoto->getContentSize() / 2); textCamera->setTextColor(Color4B::BLACK); btnCamera->addChild(textCamera); //------------------------------------- Label* textCancel = Label::createWithSystemFont("Cancel", "", 24); textCancel->setPosition(btnPhoto->getContentSize() / 2); textCancel->setTextColor(Color4B::BLACK); btnCancel->addChild(textCancel); //------------------------------------- // 准备显示 //------------------------------------- Director::getInstance()->getRunningScene()->scheduleOnce([=](float time) { Director::getInstance()->getRunningScene()->addChild(m_layer, INT_MAX); m_layer->release(); float height = sprite->getContentSize().height; sprite->setPositionY(sprite->getPositionY() - height); menu ->setPositionY(menu->getPositionY() - height); MoveBy* move = MoveBy::create(0.3, Vec2(0, height)); sprite->runAction(move); menu ->runAction(move->clone()); m_layer->setOpacity(0); m_layer->runAction(FadeTo::create(0.2, 125)); }, 0.1, "ImagePickerScheduleOnce"); //------------------------------------- // 截断事件 //------------------------------------- EventListenerTouchOneByOne* touchEvent = EventListenerTouchOneByOne::create(); touchEvent->setSwallowTouches(true); touchEvent->onTouchBegan = [=](Touch* touch, Event* eve) { if(sprite->getBoundingBox().containsPoint(touch->getLocation())) return true; float height = sprite->getContentSize().height; MoveBy* move = MoveBy::create(0.2, Vec2(0, -height)); sprite->runAction(move); menu ->runAction(move->clone()); Sequence* seq = Sequence::createWithTwoActions(FadeOut::create(0.2), RemoveSelf::create()); m_layer->runAction(seq); return true; }; Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchEvent, sprite); //------------------------------------- return true; } //-------------------------------------------------- #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) void ImagePicker::setViewController(void* viewController) { m_viewController = viewController; } #endif //-------------------------------------------------- #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) extern "C" { void Java_org_cocos2dx_cpp_ImagePicker_onImageSaved(JNIEnv* env, jobject thiz, jstring path) { std::string strPath = JniHelper::jstring2string(path); Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("ImagePickerEvent", &strPath); } } #endif //--------------------------------------------------
为工程添加资源:略
(图片资源和代码下载地址:点击打开链接)
分平台实现:
------------------------------------------
Android
------------------------------------------
1. 打开Eclipse在org.cocos2x.cpp(加入你没改过的话)包名下新建“ImagePicker.java”文件
ImagePicker.java具体代码如下:
public class ImagePicker{ <span style="white-space:pre"> </span>public static final int NONE = 0; public static final int PHOTOHRAPH = 1; // 拍照 public static final int PHOTOZOOM = 2; // 缩放 public static final int PHOTORESOULT = 3; // 结果 public static final String IMAGE_UNSPECIFIED = "image/*"; private static ImagePicker instance = null; private static Activity activity = null; public static native void onImageSaved(String path); public static ImagePicker getInstance(){ if(instance == null){ instance = new ImagePicker(); } return instance; } // 初始化 public void init(Activity activity){ ImagePicker.activity = activity; } // 打开相册 static public void openPhoto(){ Intent intent = new Intent(Intent.ACTION_PICK, null); intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_UNSPECIFIED); activity.startActivityForResult(intent, PHOTOZOOM); } // 打开相机 static public void openCamera(){ Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(activity.getFilesDir(), "@cc_cameraCache.jpg"))); activity.startActivityForResult(intent, PHOTOHRAPH); } // 回调 public void onActivityResult(int requestCode, int resultCode, Intent data){ if (resultCode == NONE) return; // 拍照 if (requestCode == PHOTOHRAPH) { File picture = new File(activity.getFilesDir() + "/@cc_cameraCache.jpg"); startPhotoZoom(Uri.fromFile(picture)); } if (data == null) return; // 读取相册缩放图片 if (requestCode == PHOTOZOOM) { startPhotoZoom(data.getData()); } // 处理结果 if (requestCode == PHOTORESOULT) { Bundle extras = data.getExtras(); if (extras != null) { Bitmap photo = extras.getParcelable("data"); ByteArrayOutputStream stream = new ByteArrayOutputStream(); photo.compress(Bitmap.CompressFormat.JPEG, 75, stream); // XXX/@ci_8888-8888-8888-8888.jpg String path = activity.getFilesDir() + "/@ci_" + UUID.randomUUID().toString() + ".jpg"; saveMyBitmap(path, photo); // 通知C++层已保存图片 并返回路径 onImageSaved(path); } } } public void startPhotoZoom(Uri uri) { Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, IMAGE_UNSPECIFIED); intent.putExtra("crop", "true"); intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); intent.putExtra("outputX", 100); intent.putExtra("outputY", 100); intent.putExtra("return-data", true); activity.startActivityForResult(intent, PHOTORESOULT); } public void saveMyBitmap(String filePath, Bitmap mBitmap){ File f = new File(filePath); try { f.createNewFile(); } catch (IOException e) { } FileOutputStream fOut = null; try { fOut = new FileOutputStream(f); } catch (FileNotFoundException e) { e.printStackTrace(); } mBitmap.compress(Bitmap.CompressFormat.JPEG, 70, fOut); try { fOut.flush(); } catch (IOException e) { e.printStackTrace(); } try { fOut.close(); } catch (IOException e) { e.printStackTrace(); } } }
2. 在Android入口进行初始化
打开AppActivity.java文件,复写OnCreate()与onActivityResult()方法。
在OnCreate方法中对我们的类初始化:“ImagePicker.getInstance().init(this);”
在onActivityResult()中将回调参数传递到ImagePicker中:“ImagePicker.getInstance().onActivityResult(requestCode, resultCode, data);”
AppActivity.java具体代码:
public class AppActivity extends Cocos2dxActivity { @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); ImagePicker.getInstance().init(this); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // TODO Auto-generated method stub super.onActivityResult(requestCode, resultCode, data); ImagePicker.getInstance().onActivityResult(requestCode, resultCode, data); } }
3. 最后在AndroidManifest.xml加入访问相机权限"<uses-permission android:name="android.permission.CAMERA"/>"
------------------------------------------
IOS
------------------------------------------
1. 将开始创建的ImagePicker.cpp文件后缀名改为.mm文件。
2. 在proj.ioc_mac/ios文件夹下创建两个文件,分别为ImagePickerViewController.h、ImagePickerViewController.mm
ImagePickerViewController.h具体代码:
#import <UIKit/UIKit.h> @interface ImagePickerViewController : UIViewController<UINavigationControllerDelegate, UIImagePickerControllerDelegate> { NSString* filePath; } // 打开本地相册 - (void)localPhoto; // 打开相机 - (void)takePhoto; @end
ImagePickerViewController.mm具体代码:
#import "ImagePickerViewController.h" #import "cocos2d.h" @interface ImagePickerViewController () @end @implementation ImagePickerViewController - (void)viewDidLoad { [super viewDidLoad]; //[self localPhoto]; } - (void)viewDidUnload { [super viewDidUnload]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } -(void)localPhoto{ UIImagePickerController *picker = [[UIImagePickerController alloc] init]; picker.delegate = self; picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; picker.allowsEditing = YES; //[self presentModalViewController:picker animated:YES]; [self presentViewController:picker animated:YES completion:^(void){ NSLog(@"Imageviewcontroller is presented"); }]; [picker release]; NSLog(@"-(void)localPhoto();"); } - (void)takePhoto{ UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera; if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { UIImagePickerController* picker = [[UIImagePickerController alloc] init]; picker.delegate = self; //设置拍照后的图像可编辑 picker.allowsEditing = YES; picker.sourceType = sourceType; [picker release]; [self presentModalViewController:picker animated:YES]; } else{ NSLog(@"模拟器中无法打开照相机,请在真机中调试"); } } - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{ NSString *type = [info objectForKey:UIImagePickerControllerMediaType]; //当选择的类型是图片 if ([type isEqualToString:@"public.image"]) { //先把图片转成NSData // UIImage* image = [info objectForKey:@"UIImagePickerControllerOriginalImage"]; UIImage* image = [info objectForKey:@"UIImagePickerControllerEditedImage"]; NSData *data; if (UIImagePNGRepresentation(image) == nil) { data = UIImageJPEGRepresentation(image, 1.0); } else { data = UIImagePNGRepresentation(image); } //图片保存的路径 //这里将图片放在沙盒的documents文件夹中 NSString * DocumentsPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; //文件管理器 NSFileManager *fileManager = [NSFileManager defaultManager]; //生成唯一字符串 NSString* uuid = [[NSUUID UUID] UUIDString]; //文件名 NSString* fileName = [NSString stringWithFormat:@"/%@.png", uuid]; //把刚刚图片转换的data对象拷贝至沙盒中 并保存为XXXXXXXX-XXXX-XXXX....XXXX.png [fileManager createDirectoryAtPath:DocumentsPath withIntermediateDirectories:YES attributes:nil error:nil]; [fileManager createFileAtPath:[DocumentsPath stringByAppendingString:fileName] contents:data attributes:nil]; //得到选择后沙盒中图片的完整路径 filePath = [[NSString alloc]initWithFormat:@"%@%@", DocumentsPath, fileName]; //关闭相册界面 [picker dismissModalViewControllerAnimated:YES]; std::string strFilePath = [filePath UTF8String]; cocos2d::Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("ImagePickerEvent", &strFilePath); } } - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker{ NSLog(@"您取消了选择图片"); [picker dismissModalViewControllerAnimated:YES]; } @end
3. 修改原工程AppController.mm文件,加入一行“ImagePicker::getInstance()->setViewController(_viewController);”用来初始化。
AppController.mm具体代码:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { cocos2d::Application *app = cocos2d::Application::getInstance(); app->initGLContextAttrs(); cocos2d::GLViewImpl::convertAttrs(); // Override point for customization after application launch. // Add the view controller's view to the window and display. window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; // Init the CCEAGLView CCEAGLView *eaglView = [CCEAGLView viewWithFrame: [window bounds] pixelFormat: (NSString*)cocos2d::GLViewImpl::_pixelFormat depthFormat: cocos2d::GLViewImpl::_depthFormat preserveBackbuffer: NO sharegroup: nil multiSampling: NO numberOfSamples: 0 ]; // Use RootViewController manage CCEAGLView _viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil]; _viewController.wantsFullScreenLayout = YES; _viewController.view = eaglView; //---------------------------------- // 初始化ImagePicker //---------------------------------- ImagePicker::getInstance()->setViewController(_viewController); //---------------------------------- // Set RootViewController to window if ( [[UIDevice currentDevice].systemVersion floatValue] < 6.0) { // warning: addSubView doesn't work on iOS6 [window addSubview: _viewController.view]; } else { // use this method on ios6 [window setRootViewController:_viewController]; } [window makeKeyAndVisible]; [[UIApplication sharedApplication] setStatusBarHidden:true]; // IMPORTANT: Setting the GLView should be done after creating the RootViewController cocos2d::GLView *glview = cocos2d::GLViewImpl::createWithEAGLView(eaglView); cocos2d::Director::getInstance()->setOpenGLView(glview); app->run(); return YES; }
OK,接下来就是在C++代码中调用了,你只需要在想使用头像选择器的时候,调用这一行代码:
ImagePicker::getInstance()->callImagePickerWithPhotoAndCamera([=](std::string path) { //做你想做的事情 });
忘了说明,参数path就是你已经选择并编辑后,图片的真实路径。
咳咳,请各位大神轻喷,喜欢游戏的程序孩纸伤不起~不说了,回家LOL去了~