Cocos2d-x 3.x 頭像選擇器,本地相簿與拍照+頭像編輯功能(Android、IOS雙平臺實現)
大連遊戲行業不是太發達,最後選擇在一家應用外包公司工作,在工作和業餘學習過程中積累了一點微不住道的經驗,希望分享給熱愛遊戲的小夥伴們。
在應用開發過程中會常常有使用者上傳頭像的功能,在網上找了N多資料發現沒有人具體介紹過該用cocos2d-x實現。這篇文章就來介紹一下如何在Android和IOS平臺上實現該功能。
先傳一張完成後的圖片一飽眼福:= = 怎麼不好用呢~
直接上程式碼:
標頭檔案 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去了~