使用 C++ 编写 Android GUI: 第 2 部分:ListView holder
关于编写 Android GUI 系列的第二部分。
引言
Wrapandroid 和 CLE 项目提供了使用多种语言(如 Python、Lua 或 C++)编写 Android GUI 应用程序的选择。本文将继续讨论使用 C++ 进行编程。对于 GUI 应用程序,ListView 控件是最常用的控件,用于向用户呈现信息并执行某些操作。为了加快 ListView 的滚动速度,通常会使用 View Holder。我们可以在 Java 中找到许多关于此的资料。但是,我们如何使用 C++ 编写 Holder 呢?
Android GUI 对象被封装在 CLE 对象中。对于每个 CLE 对象,CLE 提供了 MallocPrivateBuf
和 GetPrivateBuf
函数来为其私有缓冲区分配内存,该缓冲区可用于存放对象指针。该缓冲区将随对象一起自动释放。如下所示:
struct Holder{
void *Object1;
void *Object2;
…
}*holder;
holder = (struct Holder *)SRPInterface -> MallocPrivateBuf(…);
然后获取 Holder
holder = (struct Holder *)SRPInterface -> GetPrivateBuf(…);
创建项目
我们使用 Eclipse 和 NDK 来开发应用程序。要了解更多关于如何安装它们的信息,请参阅其他相关文章。Android 版本应高于 2.2。Wrapandroid 已更新至 0.9.0 版本,请从 http://code.google.com/p/wrapandroid-for-multilanguage 下载。CLE 已更新至 r7 版本,网站是 http://www.code.google.com/p/cle-for-android。
Wrapandroid 对象的功能与相应的 Android 对象几乎相同(约 90%)。要了解如何使用这些函数以及提供了哪些函数,请参阅 Android SDK 文档和 wrapandroid.chm 文档。
步骤
- 打开 Eclipse,创建一个名为“listviewholder”的新 Android 项目。
- CLE 可以在应用程序启动时从网络安装,在这种情况下,应添加以下权限
- 将 Java 库 starcore_android_r7.jar 和 wrapandroid.jar 复制到项目目录,并将它们添加到项目中
- 编辑 ListviewholderActivity.java 并进行如下更改,加载本地共享库。
- CLE 也可以包含在项目中,在这种情况下,您应该将 CLE 的共享库复制到项目目录中
<uses-permission android:name="android.permission.INTERNET" />0
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
import com.srplab.wrapandroid.*;
public class ListviewholderActivity extends WrapAndroidActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);
StarActivity._Call("DoFile","",
"/data/data/"+getPackageName()+"/lib/libCode.so");
}
}
并将下载标志设置为 false
public class ListviewholderActivity extends WrapAndroidActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
DownloadFromNetFlag = false;
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);
StarActivity._Call("DoFile","",
"/data/data/"+getPackageName()+"/lib/libCode.so");
}
}
List View
有很多示例解释如何创建列表视图以及如何使用 Holder 来加快滚动速度。但它们都是用 Java 编写的。我们如何用 C++ 语言进行编程?本文提供了一个示例。要创建列表视图,我们需要三个步骤。首先,使用 XML 文件创建布局。当然,您也可以动态创建布局。接下来,创建一个适配器,用于提供在每个视图项中显示的控件。最后,创建一个列表视图控件并将适配器分配给列表视图。
步骤 1:定义布局
以下是一个包含线性布局、图像视图、文本视图和按钮的布局示例。布局文件以 XML 格式编写。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5px"/>
<LinearLayout android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:textSize="22px" />
<TextView android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:textSize="13px" />
</LinearLayout>
<Button android:id="@+id/view_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="more"
android:layout_gravity="bottom|right" />
</LinearLayout>
步骤 2:创建适配器
适配器用于为列表视图提供内容。Android 定义了许多种类的适配器,例如 Cursor Adapter、Array List Adapter 等,它们都是 BaseAdapter
的子类。要创建自己的适配器,我们可以扩展 BaseAdapter
并重写其 getCount
、getItem
、getItemId
和 getView
函数。getCount
函数应返回要在列表视图中显示的项数。getItem
函数返回指定位置的对象,getItemId
返回对象的 ID。getView
是主要部分。在此函数中,我们膨胀在上述 XML 文件中定义的布局,设置布局中控件的内容,并将其提供给列表视图。
以下是一个适配器示例
创建适配器
Android 的 BaseAdapter
被封装在 BaseAdapterClass
中。我们使用 MallocObjectL
函数来实例化它,并使用 CLE 接口的 CreateOVLFunction
函数来重写其函数。
MallocObjectL
的参数是 BaseAdapterClass
的对象 ID,该 ID 在 Wrapandroid 开发文件的 C++ 源文件 SRPWrapAndroidEngine_UUIDDef.cpp 中定义。编译时必须将此文件包含在项目中。MallocObjectL
的其他参数不需要处理,我们在下面的代码中设置它们。
CreateOVLFunction
函数用于重写类中定义的函数。它的输入参数是函数 ID 和函数地址。函数 ID 也在文件 SRPWrapAndroidEngine_UUIDDef.cpp 中定义。
void *MyAdapter = SRPInterface->MallocObjectL(&VSOBJID_BaseAdapterClass,0,NULL);
// set getCount function of the adapter
SRPInterface -> CreateOVLFunction(MyAdapter,&VSFUNCID_BaseAdapterClass_getCount,(void *)MyAdapter_getCount,NULL);
// set getItem function of the adapter
SRPInterface -> CreateOVLFunction(MyAdapter,&VSFUNCID_BaseAdapterClass_getItem,(void *)MyAdapter_getItem,NULL);
// set getItemId function of the adapter
SRPInterface -> CreateOVLFunction(MyAdapter,&VSFUNCID_BaseAdapterClass_getItemId,(void *)MyAdapter_getItemId,NULL);
// set getView function of the adapter
SRPInterface -> CreateOVLFunction(MyAdapter,&VSFUNCID_BaseAdapterClass_getView,(void *)MyAdapter_getView,NULL);
getCount 函数
此函数返回将在列表视图中显示的项数。在此示例中,我们仅返回数字 20。
static VS_INT32 SRPAPI MyAdapter_getCount(void *Object)
{
return 20; // the number list view item;
}
getItem 函数
此函数返回指定位置的对象。在 Android Java 原型中,此函数的返回值是 Object
,表示任何类型的 Java 对象。但是对于 C++,我们必须返回一个明确的类型。
static VS_INT32 SRPAPI MyAdapter_getItem(void *Object,VS_INT32 Position)
{
return Position;
}
在项目下创建一个 jni 目录,将 Wrapandroid 和 CLE 的头文件复制到 jni 目录中。创建名为 code.cpp 和 Android.mk 的新文件,按如下方式编辑 Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Here we give our module name and sourcefile(s)
LOCAL_CFLAGS += -Wno-write-strings -DENV_ANDROID
LOCAL_CPPFLAGS += -Wno-write-strings -fexceptions -DENV_ANDROID
LOCAL_LDFLAGS += -Wno-write-strings -DENV_ANDROID
LOCAL_C_INCLUDES += cle_files/include
#--------source file
MODULE_CXXSRCS := Code.cpp SRPWrapAndroidEngine_UUIDDef.cpp
LOCAL_SRC_FILES := ${MODULE_CXXSRCS}
LOCAL_LDLIBS := cle_files/libs/armeabi/libstarlib.a
LOCAL_MODULE := Code
include $(BUILD_SHARED_LIBRARY)
#------------------------
include $(CLEAR_VARS)
LOCAL_SRC_FILES := cle_files/so/armeabi/libstarcore.so
LOCAL_MODULE := starcore
include $(PREBUILT_SHARED_LIBRARY)
#------------------------
include $(CLEAR_VARS)
LOCAL_SRC_FILES := cle_files/so/armeabi/libstar_java.so
LOCAL_MODULE := star_java
include $(PREBUILT_SHARED_LIBRARY)
code.cpp
在 Android 中编写本地代码,我们应该将源文件编译成共享库。共享库公开两个函数
VS_BOOL StarCoreService_Init(class ClassOfStarCore *starcore)
void StarCoreService_Term(class ClassOfStarCore *starcore)
第一个函数将在加载库时调用,可用于执行一些初始化。StarCoreService_Term
将在共享库卸载之前被调用。code.cpp 的代码如下所示。我们将详细介绍。
#include "SRPWrapAndroidEngine_VSDHeader.h"
static class ClassOfSRPInterface *SRPInterface;
static void *StarActivity;
static void *mInflater;
// event listener function of button click,
static VS_INT32 MyButton_onClick(VS_ULONG FunctionChoice,void *EventPara)
{
//create a toast and show some information
void *toast = SRPInterface->MallocObjectL(&VSOBJID_ToastClass,0,NULL);
SRPInterface -> ScriptCall(toast,NULL,"makeText","(si)","Button is click", 0);
SRPInterface -> ScriptCall(toast,NULL,"show","()");
return 0;
}
static VS_INT32 SRPAPI MyAdapter_getCount(void *Object)
{
return 20; // the number list view item;
}
static VS_INT32 SRPAPI MyAdapter_getItem(void *Object,VS_INT32 Position)
{
return Position;
}
static VS_LONG SRPAPI MyAdapter_getItemId(void *Object,VS_INT32 Position)
{
return Position;
}
static VS_OBJPTR SRPAPI MyAdapter_getView(void *Object,
VS_INT32 position,VS_OBJPTR convertView,VS_OBJPTR parent)
{
void *i;
// define holder, which contains image, title, info,
// and button objects. The objects are c pointers.
struct MyHolder{
void *img;
void *title;
void *info;
void *view_btn;
}*holder;
if( convertView == NULL ){
// get resource id contains in the project.
int vlist2 = SRPInterface -> ScriptCall(StarActivity,NULL,
"getResource","(s)i","layout/vlist2");
// inflate widgets from resource, using the inflate function of mInflater
convertView = (void *)SRPInterface -> ScriptCall(mInflater,NULL,"inflate",
"(sio)o","LinearLayoutClass",vlist2, NULL);
// Alloc object’s private buf which is used as holder.
holder = (struct MyHolder *)SRPInterface -> MallocPrivateBuf(convertView,
SRPInterface -> GetLayer(convertView),0,sizeof(struct MyHolder));
// get image resource id contains in the layout.
int img = SRPInterface -> ScriptCall(StarActivity,NULL,
"getResource","(s)i","id/img");
// get image widgets, and store the pointer to holder.
holder -> img = (void *)SRPInterface -> ScriptCall(convertView,NULL,
"findViewById","(si)o","ImageViewClass", img);
// get text resource id contains in the layout.
int title = SRPInterface -> ScriptCall(StarActivity,NULL,
"getResource","(s)i","id/title");
// get textview widgets, and store the pointer to holder.
holder -> title = (void *)SRPInterface -> ScriptCall(convertView,NULL,
"findViewById","(si)o","TextViewClass", title);
// get text resource id contains in the layout.
int info = SRPInterface -> ScriptCall(StarActivity,NULL,
"getResource","(s)i","id/info");
// get textview widgets, and store the pointer to holder.
holder -> info = (void *)SRPInterface -> ScriptCall(convertView,NULL,
"findViewById","(si)o","TextViewClass", info);
// get button resource id contains in the layout.
int view_btn = SRPInterface -> ScriptCall(StarActivity,NULL,
"getResource","(s)i","id/view_btn");
// get button widgets, and store the pointer to holder.
holder -> view_btn = (void *)SRPInterface -> ScriptCall(convertView,NULL,
"findViewById","(si)o","ButtonClass", view_btn);
}else{
// if convertVew is not null, then we can fetch the holder stored in the object private buf.
holder = (struct MyHolder *)SRPInterface -> GetPrivateBuf(convertView,
SRPInterface -> GetLayer(convertView),0,NULL);
}
int resid;
switch( position % 3 ){
case 0 :
// get resource id “drawable/i1” in the project
resid = SRPInterface -> ScriptCall(StarActivity,NULL,
"getResource","(s)i","drawable/i1");
// set the image to image widget
SRPInterface -> ScriptCall(holder -> img,NULL,"setBackgroundResource","(i)",resid);
// set text
SRPInterface -> ScriptCall(holder -> title,NULL,"setText","(s)","G1");
// set text
SRPInterface -> ScriptCall(holder -> info,NULL,"setText","(s)","google 1");
break;
case 1 :
resid = SRPInterface -> ScriptCall(StarActivity,NULL,"getResource","(s)i","drawable/i2");
SRPInterface -> ScriptCall(holder -> img,NULL,"setBackgroundResource","(i)",resid);
SRPInterface -> ScriptCall(holder -> title,NULL,"setText","(s)","G2");
SRPInterface -> ScriptCall(holder -> info,NULL,"setText","(s)","google 2");
break;
case 2 :
resid = SRPInterface -> ScriptCall(StarActivity,NULL,"getResource","(s)i","drawable/i3");
SRPInterface -> ScriptCall(holder -> img,NULL,"setBackgroundResource","(i)",resid);
SRPInterface -> ScriptCall(holder -> title,NULL,"setText","(s)","G3");
SRPInterface -> ScriptCall(holder -> info,NULL,"setText","(s)","google 3");
break;
}
// set onClick event listener
SRPInterface -> RegEventFunction(holder -> view_btn,&VSOUTEVENTID_ViewClass_onClick,
holder -> view_btn,(void *)MyButton_onClick,0);
SRPInterface -> ScriptCall(holder -> view_btn,NULL,"setOnClickListener","()");
return convertView;
}
// init function
VS_BOOL StarCoreService_Init(class ClassOfStarCore *starcore)
{
class ClassOfBasicSRPInterface *BasicSRPInterface;
//--init star core
BasicSRPInterface = starcore ->GetBasicInterface();
SRPInterface = BasicSRPInterface ->GetSRPInterface(
BasicSRPInterface->QueryActiveService(NULL),"","");
void *ActivityClass;
ActivityClass = SRPInterface -> GetObjectEx(NULL,"ActivityClass");
// get current activity
StarActivity = (void *)SRPInterface -> ScriptCall(ActivityClass,NULL,"getCurrent","()O");
SRPInterface -> Print("Get Main Activity = %s", SRPInterface -> GetName(StarActivity));
//--create AbsoluteLayout
void *MyLayout = SRPInterface->MallocObject(StarActivity,VSATTRINDEX_ACTIVITYCLASS_VIEWGROUPQUEUE,
&VSOBJID_AbsoluteLayoutClass,0,NULL);
// create a layout inflater
mInflater = SRPInterface->MallocObjectL(&VSOBJID_LayoutInflaterClass,0,NULL);
// create an adapter
void *MyAdapter = SRPInterface->MallocObjectL(&VSOBJID_BaseAdapterClass,0,NULL);
// set getCount function of the adapter
SRPInterface -> CreateOVLFunction(MyAdapter,&VSFUNCID_BaseAdapterClass_getCount,(void *)MyAdapter_getCount,NULL);
// set getItem function of the adapter
SRPInterface -> CreateOVLFunction(MyAdapter,&VSFUNCID_BaseAdapterClass_getItem,(void *)MyAdapter_getItem,NULL);
// set getItemId function of the adapter
SRPInterface -> CreateOVLFunction(MyAdapter,&VSFUNCID_BaseAdapterClass_getItemId,(void *)MyAdapter_getItemId,NULL);
// set getView function of the adapter
SRPInterface -> CreateOVLFunction(MyAdapter,&VSFUNCID_BaseAdapterClass_getView,(void *)MyAdapter_getView,NULL);
// Create a listview
void *MyListView = SRPInterface->MallocObject(MyLayout,VSATTRINDEX_VIEWGROUPCLASS_VIEWQUEUE,&VSOBJID_ListViewClass,0,NULL);
// set listview layout parameter
SRPInterface -> ScriptCall(MyListView,NULL,"setAbsoluteLayoutParams",
"(iiii)",FILL_PARENT,FILL_PARENT,0,0);
// set listview’s adapter
SRPInterface -> ScriptCall(MyListView,NULL,"setAdapter","(o)",MyAdapter);
return VS_TRUE;
}
void StarCoreService_Term(class ClassOfStarCore *starcore)
{
SRPInterface -> Release();
return;
}