cocos2d-x官方自带的输入框,简直惨不忍睹,在ios还好,在安卓简直了。。用过的都知道。。。
所以为了用户体验,我们自己搞一个吧。输入框这种东西比较特殊,不像按钮、列表框之类的很容易实现,因为涉及到复制粘贴、操作虚拟键盘等,所以当然是用安卓原生的输入框最好了。
非常感谢我们的主程陈剑大哥,思路是陈剑大哥想的,我只负责记录一下。
本来代码50天前就写好了,但是绑定到js一直失败,恶心了好几天放弃了。前几天在用lua写点东西,把之前的代码拷过来试着绑定到lua,结果一次就成功了,完全没有踩坑,不知道说什么好了。。而且前几天官方发布了cocos2d-x 3.8版本,已经自带了,所以现在再发出来感觉毫无竞争力了呢。。。
使用原生控件的优点不用多说了,缺点当然也是有的。例如排版、布局、屏幕适配等,都要适应cocos2d的规则,而且由于是系统控件,渲染层级不受cocos2d的zorder的影响。当然这些努力一下也是可以解决的。
要注意的有:
1、c++通过jni控制java创建安卓控件。
2、c++持有java对象的引用,防止垃圾回收。
3、java回调c语言,c语言调用c++,所以需要处理好java对象与c++对象的对应关系。
我的做法是:
1、创建一个输入框工厂类,负责根据不同平台生成输入框,当然我只会安卓的。
2、工厂类维护一个map,保存c++对象和java对象的对应关系。
3、根据cocos2d的世界坐标系,更新安卓原生控件。
防止恶意转载还改名的恶心行为。本博客地址:http://www.cnblogs.com/wolfred7464/
上代码吧:
1 #ifndef __CocosAndroidStudio__EditText__ 2 #define __CocosAndroidStudio__EditText__ 3 4 #include <string> 5 #include "cocos2d.h" 6 7 class EditText : public cocos2d::Node { 8 public: 9 virtual ~EditText() {}; 10 virtual void setString(const std::string& str) = 0; 11 virtual std::string getString() = 0; 12 13 protected: 14 15 16 }; 17 18 #endif /* defined(__CocosAndroidStudio__EditText__) */
1 #ifndef __CocosAndroidStudio__EditTextAndroid__ 2 #define __CocosAndroidStudio__EditTextAndroid__ 3 4 #include "EditText.h" 5 #include "platform/android/jni/JniHelper.h" 6 #include "EditTextFactory.h" 7 8 class EditTextAndroid : public EditText { 9 public: 10 ~EditTextAndroid(); 11 12 virtual void setString(const std::string& str) override; 13 virtual std::string getString() override; 14 15 virtual void setPosition(const cocos2d::Vec2& pos) override; 16 virtual void setContentSize(const cocos2d::Size& size) override; 17 18 virtual void onEnter() override; 19 20 friend class EditTextFactory; 21 22 private: 23 EditTextAndroid(); 24 static EditTextAndroid* create(); 25 26 cocos2d::JniMethodInfo getJavaMethod(const char* name, const char* param); 27 void setPositionAndroid(); 28 void setContentSizeAndroid(); 29 30 jobject _object; 31 }; 32 33 #endif /* defined(__CocosAndroidStudio__EditTextAndroid__) */
1 #include "EditTextAndroid.h" 2 #include "cocos2d.h" 3 #include <unistd.h> 4 #include <string> 5 6 USING_NS_CC; 7 using namespace std; 8 9 EditTextAndroid* EditTextAndroid::create() { 10 auto ret = new EditTextAndroid(); 11 ret->autorelease(); 12 return ret; 13 } 14 15 EditTextAndroid::EditTextAndroid() { 16 auto env = cocos2d::JniHelper::getEnv(); 17 18 jclass cls = env->FindClass("org/red/tools/RedEditText"); 19 assert(cls != NULL); 20 21 jmethodID ctor = env->GetMethodID(cls, "<init>", "()V"); 22 assert(ctor != NULL); 23 24 this->_object = env->NewObject(cls, ctor); 25 env->NewGlobalRef(this->_object); 26 } 27 28 EditTextAndroid::~EditTextAndroid() { 29 auto env = cocos2d::JniHelper::getEnv(); 30 env->DeleteGlobalRef(this->_object); 31 } 32 33 JniMethodInfo EditTextAndroid::getJavaMethod(const char* name, const char* param) { 34 JniMethodInfo info; 35 bool isHave = JniHelper::getMethodInfo(info, "org/red/tools/RedEditText", name, param); 36 assert(isHave); 37 return info; 38 } 39 40 void EditTextAndroid::setString(const std::string& str) { 41 auto info = getJavaMethod("setString", "(Ljava/lang/String;)V"); 42 jstring jstr = info.env->NewStringUTF(str.c_str()); 43 info.env->CallVoidMethod(this->_object, info.methodID, jstr); 44 } 45 46 std::string EditTextAndroid::getString() { 47 auto info = getJavaMethod("getString", "()Ljava/lang/String;"); 48 auto jstr = (jstring)info.env->CallObjectMethod(this->_object, info.methodID); 49 50 jboolean isCopy; 51 auto chars = info.env->GetStringUTFChars(jstr, &isCopy); 52 std::string str(chars); 53 if(isCopy == JNI_TRUE) { 54 info.env->ReleaseStringUTFChars(jstr, chars); 55 } 56 info.env->DeleteLocalRef(jstr); 57 return str; 58 } 59 60 void EditTextAndroid::setPosition(const Vec2& pos) { 61 EditText::setPosition(pos); 62 setPositionAndroid(); 63 } 64 65 void EditTextAndroid::setContentSize(const Size& size) { 66 EditText::setContentSize(size); 67 setContentSizeAndroid(); 68 } 69 70 void EditTextAndroid::setPositionAndroid() { 71 if(isRunning()) { 72 auto pos = convertToWorldSpace(Vec2(0, getContentSize().height)); 73 auto info = getJavaMethod("setPosition", "(II)V"); 74 auto visHeight = Director::getInstance()->getVisibleSize().height; 75 info.env->CallVoidMethod(this->_object, info.methodID, jint(pos.x), jint(visHeight - pos.y)); 76 } 77 } 78 79 void EditTextAndroid::setContentSizeAndroid() { 80 if(isRunning()) { 81 auto realSize = SizeApplyAffineTransform(getContentSize(), getNodeToWorldAffineTransform()); 82 auto info = getJavaMethod("setContentSize", "(II)V"); 83 info.env->CallVoidMethod(this->_object, info.methodID, jint(realSize.width), jint(realSize.height)); 84 } 85 } 86 87 void EditTextAndroid::onEnter() { 88 EditText::onEnter(); 89 setPositionAndroid(); 90 setContentSizeAndroid(); 91 } 92 93 extern "C" { 94 JNIEXPORT void JNICALL Java_org_red_tools_RedEditText_nativeOnTextChanged(JNIEnv *env, jobject javaThis, jstring jstr) { 95 jboolean isCopy; 96 auto chars = env->GetStringUTFChars(jstr, &isCopy); 97 std::string str(chars); 98 if(isCopy == JNI_TRUE) { 99 env->ReleaseStringUTFChars(jstr, chars); 100 } 101 log("[Red] textChanged: %s", str.c_str()); 102 } 103 }
1 #ifndef __CocosAndroidStudio__EditTextFactory__ 2 #define __CocosAndroidStudio__EditTextFactory__ 3 4 #include "EditText.h" 5 #include <map> 6 #include "platform/android/jni/JniHelper.h" 7 8 class EditTextFactory { 9 public: 10 static EditTextFactory* getInstance(); 11 12 EditText* createEditText(); 13 EditText* getEditTextByJobject(jobject obj); 14 15 private: 16 EditTextFactory(); 17 18 std::map<jobject, EditText*> _map; 19 }; 20 21 #endif /* defined(__CocosAndroidStudio__EditTextFactory__) */
1 #include "EditTextFactory.h" 2 #include "EditTextAndroid.h" 3 #include <string> 4 #include "cocos2d.h" 5 6 EditTextFactory::EditTextFactory() {} 7 8 EditTextFactory* EditTextFactory::getInstance() { 9 static EditTextFactory instance; 10 return &instance; 11 } 12 13 EditText* EditTextFactory::createEditText() { 14 #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) 15 auto edit = EditTextAndroid::create(); 16 auto jobj = edit->_object; 17 _map.insert(std::pair<jobject, EditText*>(jobj, edit)); 18 return edit; 19 #endif 20 21 return nullptr; 22 } 23 24 EditText* EditTextFactory::getEditTextByJobject(jobject obj) { 25 return _map[obj]; 26 }
1 package org.red.tools; 2 3 import org.cocos2dx.lib.Cocos2dxActivity; 4 5 import android.graphics.Color; 6 import android.os.Handler; 7 import android.os.Looper; 8 import android.text.Editable; 9 import android.text.TextWatcher; 10 import android.util.Log; 11 import android.view.inputmethod.EditorInfo; 12 import android.widget.EditText; 13 import android.widget.FrameLayout; 14 15 public class RedEditText { 16 17 private EditText _edit; 18 19 public RedEditText() { 20 new Handler(Looper.getMainLooper()).post(new Runnable() { 21 @Override 22 public void run() { 23 RedEditText.this._edit = new EditText(Cocos2dxActivity.getContext()); 24 EditText edit = RedEditText.this._edit; 25 //edit.setBackgroundDrawable(null); 26 edit.setTextColor(Color.rgb(255, 255, 255)); 27 edit.setMaxLines(1); 28 edit.setSingleLine(); 29 edit.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); 30 Cocos2dxActivity activity = (Cocos2dxActivity)(Cocos2dxActivity.getContext()); 31 FrameLayout root = (FrameLayout)activity.findViewById(android.R.id.content).getRootView(); 32 root.addView(edit); 33 34 edit.addTextChangedListener(new TextWatcher() { 35 @Override 36 public void onTextChanged(CharSequence s, int start, int before, int count) { 37 nativeOnTextChanged(s.toString()); 38 } 39 40 @Override 41 public void beforeTextChanged(CharSequence s, int start, int count, int after) {} 42 @Override 43 public void afterTextChanged(Editable s) {} 44 }); 45 } 46 }); 47 } 48 49 public void setString(final String str) { 50 Log.e("red", str); 51 new Handler(Looper.getMainLooper()).post(new Runnable() { 52 @Override 53 public void run() { 54 _edit.setText(str); 55 } 56 }); 57 } 58 59 public String getString() { 60 return _edit.getText().toString(); 61 } 62 63 public void setContentSize(final int w, final int h) { 64 new Handler(Looper.getMainLooper()).post(new Runnable() { 65 @Override 66 public void run() { 67 FrameLayout.LayoutParams params = (FrameLayout.LayoutParams)_edit.getLayoutParams(); 68 params.width = w; 69 params.height = h; 70 _edit.setLayoutParams(params); 71 } 72 }); 73 } 74 75 public void setPosition(final int x, final int y) { 76 new Handler(Looper.getMainLooper()).post(new Runnable() { 77 @Override 78 public void run() { 79 FrameLayout.LayoutParams params = (FrameLayout.LayoutParams)_edit.getLayoutParams(); 80 params.leftMargin = x; 81 params.topMargin = y; 82 _edit.setLayoutParams(params); 83 } 84 }); 85 } 86 87 private native void nativeOnTextChanged(String s); 88 89 }
最后是luabinding,吐血推荐教程:http://www.cocos.com/doc/tutorial/show?id=1295
这是我绑定之后的代码,可以直接用:
1 #include "base/ccConfig.h" 2 #if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 3 #ifndef __RedBindings_h__ 4 #define __RedBindings_h__ 5 6 #ifdef __cplusplus 7 extern "C" { 8 #endif 9 #include "tolua++.h" 10 #ifdef __cplusplus 11 } 12 #endif 13 14 int register_all_RedBindings(lua_State* tolua_S); 15 16 17 18 19 20 21 22 23 24 #endif // __RedBindings_h__ 25 #endif //#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
1 #include "lua_RedBindings_auto.hpp" 2 #if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 3 #include "RedBindings.h" 4 #include "tolua_fix.h" 5 #include "LuaBasicConversions.h" 6 7 8 int lua_RedBindings_EditText_setString(lua_State* tolua_S) 9 { 10 int argc = 0; 11 EditText* cobj = nullptr; 12 bool ok = true; 13 14 #if COCOS2D_DEBUG >= 1 15 tolua_Error tolua_err; 16 #endif 17 18 19 #if COCOS2D_DEBUG >= 1 20 if (!tolua_isusertype(tolua_S,1,"EditText",0,&tolua_err)) goto tolua_lerror; 21 #endif 22 23 cobj = (EditText*)tolua_tousertype(tolua_S,1,0); 24 25 #if COCOS2D_DEBUG >= 1 26 if (!cobj) 27 { 28 tolua_error(tolua_S,"invalid 'cobj' in function 'lua_RedBindings_EditText_setString'", nullptr); 29 return 0; 30 } 31 #endif 32 33 argc = lua_gettop(tolua_S)-1; 34 if (argc == 1) 35 { 36 std::string arg0; 37 38 ok &= luaval_to_std_string(tolua_S, 2,&arg0, "EditText:setString"); 39 if(!ok) 40 { 41 tolua_error(tolua_S,"invalid arguments in function 'lua_RedBindings_EditText_setString'", nullptr); 42 return 0; 43 } 44 cobj->setString(arg0); 45 lua_settop(tolua_S, 1); 46 return 1; 47 } 48 luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d ", "EditText:setString",argc, 1); 49 return 0; 50 51 #if COCOS2D_DEBUG >= 1 52 tolua_lerror: 53 tolua_error(tolua_S,"#ferror in function 'lua_RedBindings_EditText_setString'.",&tolua_err); 54 #endif 55 56 return 0; 57 } 58 int lua_RedBindings_EditText_getString(lua_State* tolua_S) 59 { 60 int argc = 0; 61 EditText* cobj = nullptr; 62 bool ok = true; 63 64 #if COCOS2D_DEBUG >= 1 65 tolua_Error tolua_err; 66 #endif 67 68 69 #if COCOS2D_DEBUG >= 1 70 if (!tolua_isusertype(tolua_S,1,"EditText",0,&tolua_err)) goto tolua_lerror; 71 #endif 72 73 cobj = (EditText*)tolua_tousertype(tolua_S,1,0); 74 75 #if COCOS2D_DEBUG >= 1 76 if (!cobj) 77 { 78 tolua_error(tolua_S,"invalid 'cobj' in function 'lua_RedBindings_EditText_getString'", nullptr); 79 return 0; 80 } 81 #endif 82 83 argc = lua_gettop(tolua_S)-1; 84 if (argc == 0) 85 { 86 if(!ok) 87 { 88 tolua_error(tolua_S,"invalid arguments in function 'lua_RedBindings_EditText_getString'", nullptr); 89 return 0; 90 } 91 std::string ret = cobj->getString(); 92 tolua_pushcppstring(tolua_S,ret); 93 return 1; 94 } 95 luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d ", "EditText:getString",argc, 0); 96 return 0; 97 98 #if COCOS2D_DEBUG >= 1 99 tolua_lerror: 100 tolua_error(tolua_S,"#ferror in function 'lua_RedBindings_EditText_getString'.",&tolua_err); 101 #endif 102 103 return 0; 104 } 105 static int lua_RedBindings_EditText_finalize(lua_State* tolua_S) 106 { 107 printf("luabindings: finalizing LUA object (EditText)"); 108 return 0; 109 } 110 111 int lua_register_RedBindings_EditText(lua_State* tolua_S) 112 { 113 tolua_usertype(tolua_S,"EditText"); 114 tolua_cclass(tolua_S,"EditText","EditText","cc.Node",nullptr); 115 116 tolua_beginmodule(tolua_S,"EditText"); 117 tolua_function(tolua_S,"setString",lua_RedBindings_EditText_setString); 118 tolua_function(tolua_S,"getString",lua_RedBindings_EditText_getString); 119 tolua_endmodule(tolua_S); 120 std::string typeName = typeid(EditText).name(); 121 g_luaType[typeName] = "EditText"; 122 g_typeCast["EditText"] = "EditText"; 123 return 1; 124 } 125 126 int lua_RedBindings_EditTextFactory_getEditTextByJobject(lua_State* tolua_S) 127 { 128 int argc = 0; 129 EditTextFactory* cobj = nullptr; 130 bool ok = true; 131 132 #if COCOS2D_DEBUG >= 1 133 tolua_Error tolua_err; 134 #endif 135 136 137 #if COCOS2D_DEBUG >= 1 138 if (!tolua_isusertype(tolua_S,1,"EditTextFactory",0,&tolua_err)) goto tolua_lerror; 139 #endif 140 141 cobj = (EditTextFactory*)tolua_tousertype(tolua_S,1,0); 142 143 #if COCOS2D_DEBUG >= 1 144 if (!cobj) 145 { 146 tolua_error(tolua_S,"invalid 'cobj' in function 'lua_RedBindings_EditTextFactory_getEditTextByJobject'", nullptr); 147 return 0; 148 } 149 #endif 150 151 argc = lua_gettop(tolua_S)-1; 152 if (argc == 1) 153 { 154 _jobject* arg0; 155 156 ok &= luaval_to_object<_jobject>(tolua_S, 2, "_jobject",&arg0, "EditTextFactory:getEditTextByJobject"); 157 if(!ok) 158 { 159 tolua_error(tolua_S,"invalid arguments in function 'lua_RedBindings_EditTextFactory_getEditTextByJobject'", nullptr); 160 return 0; 161 } 162 EditText* ret = cobj->getEditTextByJobject(arg0); 163 object_to_luaval<EditText>(tolua_S, "EditText",(EditText*)ret); 164 return 1; 165 } 166 luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d ", "EditTextFactory:getEditTextByJobject",argc, 1); 167 return 0; 168 169 #if COCOS2D_DEBUG >= 1 170 tolua_lerror: 171 tolua_error(tolua_S,"#ferror in function 'lua_RedBindings_EditTextFactory_getEditTextByJobject'.",&tolua_err); 172 #endif 173 174 return 0; 175 } 176 int lua_RedBindings_EditTextFactory_createEditText(lua_State* tolua_S) 177 { 178 int argc = 0; 179 EditTextFactory* cobj = nullptr; 180 bool ok = true; 181 182 #if COCOS2D_DEBUG >= 1 183 tolua_Error tolua_err; 184 #endif 185 186 187 #if COCOS2D_DEBUG >= 1 188 if (!tolua_isusertype(tolua_S,1,"EditTextFactory",0,&tolua_err)) goto tolua_lerror; 189 #endif 190 191 cobj = (EditTextFactory*)tolua_tousertype(tolua_S,1,0); 192 193 #if COCOS2D_DEBUG >= 1 194 if (!cobj) 195 { 196 tolua_error(tolua_S,"invalid 'cobj' in function 'lua_RedBindings_EditTextFactory_createEditText'", nullptr); 197 return 0; 198 } 199 #endif 200 201 argc = lua_gettop(tolua_S)-1; 202 if (argc == 0) 203 { 204 if(!ok) 205 { 206 tolua_error(tolua_S,"invalid arguments in function 'lua_RedBindings_EditTextFactory_createEditText'", nullptr); 207 return 0; 208 } 209 EditText* ret = cobj->createEditText(); 210 object_to_luaval<EditText>(tolua_S, "EditText",(EditText*)ret); 211 return 1; 212 } 213 luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d ", "EditTextFactory:createEditText",argc, 0); 214 return 0; 215 216 #if COCOS2D_DEBUG >= 1 217 tolua_lerror: 218 tolua_error(tolua_S,"#ferror in function 'lua_RedBindings_EditTextFactory_createEditText'.",&tolua_err); 219 #endif 220 221 return 0; 222 } 223 int lua_RedBindings_EditTextFactory_getInstance(lua_State* tolua_S) 224 { 225 int argc = 0; 226 bool ok = true; 227 228 #if COCOS2D_DEBUG >= 1 229 tolua_Error tolua_err; 230 #endif 231 232 #if COCOS2D_DEBUG >= 1 233 if (!tolua_isusertable(tolua_S,1,"EditTextFactory",0,&tolua_err)) goto tolua_lerror; 234 #endif 235 236 argc = lua_gettop(tolua_S) - 1; 237 238 if (argc == 0) 239 { 240 if(!ok) 241 { 242 tolua_error(tolua_S,"invalid arguments in function 'lua_RedBindings_EditTextFactory_getInstance'", nullptr); 243 return 0; 244 } 245 EditTextFactory* ret = EditTextFactory::getInstance(); 246 object_to_luaval<EditTextFactory>(tolua_S, "EditTextFactory",(EditTextFactory*)ret); 247 return 1; 248 } 249 luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d ", "EditTextFactory:getInstance",argc, 0); 250 return 0; 251 #if COCOS2D_DEBUG >= 1 252 tolua_lerror: 253 tolua_error(tolua_S,"#ferror in function 'lua_RedBindings_EditTextFactory_getInstance'.",&tolua_err); 254 #endif 255 return 0; 256 } 257 static int lua_RedBindings_EditTextFactory_finalize(lua_State* tolua_S) 258 { 259 printf("luabindings: finalizing LUA object (EditTextFactory)"); 260 return 0; 261 } 262 263 int lua_register_RedBindings_EditTextFactory(lua_State* tolua_S) 264 { 265 tolua_usertype(tolua_S,"EditTextFactory"); 266 tolua_cclass(tolua_S,"EditTextFactory","EditTextFactory","",nullptr); 267 268 tolua_beginmodule(tolua_S,"EditTextFactory"); 269 tolua_function(tolua_S,"getEditTextByJobject",lua_RedBindings_EditTextFactory_getEditTextByJobject); 270 tolua_function(tolua_S,"createEditText",lua_RedBindings_EditTextFactory_createEditText); 271 tolua_function(tolua_S,"getInstance", lua_RedBindings_EditTextFactory_getInstance); 272 tolua_endmodule(tolua_S); 273 std::string typeName = typeid(EditTextFactory).name(); 274 g_luaType[typeName] = "EditTextFactory"; 275 g_typeCast["EditTextFactory"] = "EditTextFactory"; 276 return 1; 277 } 278 279 static int lua_RedBindings_EditTextAndroid_finalize(lua_State* tolua_S) 280 { 281 printf("luabindings: finalizing LUA object (EditTextAndroid)"); 282 return 0; 283 } 284 285 int lua_register_RedBindings_EditTextAndroid(lua_State* tolua_S) 286 { 287 tolua_usertype(tolua_S,"EditTextAndroid"); 288 tolua_cclass(tolua_S,"EditTextAndroid","EditTextAndroid","EditText",nullptr); 289 290 tolua_beginmodule(tolua_S,"EditTextAndroid"); 291 tolua_endmodule(tolua_S); 292 std::string typeName = typeid(EditTextAndroid).name(); 293 g_luaType[typeName] = "EditTextAndroid"; 294 g_typeCast["EditTextAndroid"] = "EditTextAndroid"; 295 return 1; 296 } 297 TOLUA_API int register_all_RedBindings(lua_State* tolua_S) 298 { 299 tolua_open(tolua_S); 300 301 tolua_module(tolua_S,"red",0); 302 tolua_beginmodule(tolua_S,"red"); 303 304 lua_register_RedBindings_EditText(tolua_S); 305 lua_register_RedBindings_EditTextFactory(tolua_S); 306 lua_register_RedBindings_EditTextAndroid(tolua_S); 307 308 tolua_endmodule(tolua_S); 309 return 1; 310 } 311 312 #endif