65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C++ 编写 Android GUI: 第 2 部分:ListView holder

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.20/5 (6投票s)

2012年6月10日

CPOL

4分钟阅读

viewsIcon

32306

downloadIcon

963

关于编写 Android GUI 系列的第二部分。

引言

Wrapandroid 和 CLE 项目提供了使用多种语言(如 Python、Lua 或 C++)编写 Android GUI 应用程序的选择。本文将继续讨论使用 C++ 进行编程。对于 GUI 应用程序,ListView 控件是最常用的控件,用于向用户呈现信息并执行某些操作。为了加快 ListView 的滚动速度,通常会使用 View Holder。我们可以在 Java 中找到许多关于此的资料。但是,我们如何使用 C++ 编写 Holder 呢?

Android GUI 对象被封装在 CLE 对象中。对于每个 CLE 对象,CLE 提供了 MallocPrivateBufGetPrivateBuf 函数来为其私有缓冲区分配内存,该缓冲区可用于存放对象指针。该缓冲区将随对象一起自动释放。如下所示:

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 文档。

步骤

  1. 打开 Eclipse,创建一个名为“listviewholder”的新 Android 项目。
  2. CLE 可以在应用程序启动时从网络安装,在这种情况下,应添加以下权限
  3. <uses-permission android:name="android.permission.INTERNET" />0
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
  4. 将 Java 库 starcore_android_r7.jarwrapandroid.jar 复制到项目目录,并将它们添加到项目中
  5. 编辑 ListviewholderActivity.java 并进行如下更改,加载本地共享库。
  6. 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");
        }
    }
  7. CLE 也可以包含在项目中,在这种情况下,您应该将 CLE 的共享库复制到项目目录中
  8. 并将下载标志设置为 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 并重写其 getCountgetItemgetItemIdgetView 函数。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.cppAndroid.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;
}

屏幕截图

© . All rights reserved.