Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Writing an Android GUI using C++: Part 2: ListView holder

4.20/5 (6 votes)
10 Jun 2012CPOL4 min read 31.6K   928  
The second part of a series on writing an Android GUI.

Introduction

Wrapandroid and the CLE project give a choice to write Android GUI applications with multiple languages, such as Python, lua, or C++. This article continues to talk about programming using C++. For GUI applications, the list view control is the most commonly used control, which is used to present information to users to perform some action. For a list view, to speed up scroll speed, a view holder is often used. We can find many materials talking about it in Java. But how do we write a holder using C++?

The Android GUI object is wrapped with a CLE object. For each CLE object, CLE presents the functions MallocPrivateBuf and GetPrivateBuf to malloc its private buf, which can be used to contain object pointers. The buffer will be freed with the object automatically. Like this: 

C++
struct Holder{
	void *Object1;
	void *Object2;
	…
    }*holder;
holder = (struct Holder *)SRPInterface -> MallocPrivateBuf(…);

and then get the holder:

C++
holder = (struct Holder *)SRPInterface -> GetPrivateBuf(…);  

Create Project  

We are using Eclipse and NDK to develop the application. To know more on how to install them, please refer to the other related articles. The Android version should be above 2.2. Wrapandroid has been updated to version 0.9.0, please download it from http://code.google.com/p/wrapandroid-for-multilanguage. CLE has benn updated to version r7, the website is http://www.code.google.com/p/cle-for-android.

The functions of the Wrapandroid object are nearly 90% same with the corresponding Android object. To learn about how to use these functions and which functions are presented, please refer to the Android SDK document and the wrapandroid.chm document. 

Steps

  1. Open Eclipse, create a new Android project named “listviewholder”.
  2. CLE may be installed from the network when application starts, in this case, the following permission should be added:
  3. XML
    <uses-permission android:name="android.permission.INTERNET" />0
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
  4. Copy the Java libraries starcore_android_r7.jar and wrapandroid.jar to the project directory, and add them into the project:
  5. Edit ListviewholderActivity.java and change as follows, loading the native share library.
  6. Java
    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. The CLE may also be included in the project, in this case, you should copy the share libraries of CLE to the directory of the project:
  8. And change the download flag to false:

    Java
    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

There are many examples to explain how to create list views and how to use a holder to speed up scroll. But they are all written with Java. How do we program with the C++ language? This article gives an example. To create a list view, we need three steps. First, create a layout using an XML file. Of course  you can create the layout dynamically. Next, create an adapter which is used to provide widgets shown in each view item. And finally, create a list view widget and assign the adapter to the list view. 

Step 1: Define layout

The following is an example of the layout which includes linear layout, image view, text view, and button. The layout file is written in XML format.

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>

Step 2: Create adapter 

An adapter is used to provide the contents for the list view. Android defines many kinds of adapters such as cursor adapter, array list adapter, etc., which are subclassed from BaseAdapter. To create our own adapter, we can extend BaseAdapter and override its functions getCount, getItem, getItemId, and getView. The getCount function should return the number of items to be displayed in the list view. The getItem function returns the object at a special position and getItemId returns the ID of the object. getView is the main part. In this function, we inflate the layout defined in the above XML file, set the contents of the widgets in the layout, and provide it to the list view.

The following is example of an adapter:

Create an adapter

The Android BaseAdapter is wrapped with BaseAdapterClass. We create an instance of it using the function MallocObjectL, and using the function CreateOVLFunction of the CLE interface to override its functions.

The  parameter of MallocObjectL is an object id of BaseAdapterClass, which is defined in the C++ source file SRPWrapAndroidEngine_UUIDDef.cpp of the Wrapandroid development files. This file must be included in the project when compiling. Other parameters of MallocObjectL do not need to be taken care of, and we set them as in the following code.

The CreateOVLFunction function is used to override functions defined in the class. The input parameter of it is a function ID and function address. The function ID is also defined in the file SRPWrapAndroidEngine_UUIDDef.cpp.

C++
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 function 

This function returns the number of items to be shown in the list view. In this example, we simply return a number 20. 

C++
static VS_INT32 SRPAPI MyAdapter_getCount(void *Object)
{
    return 20;  // the number list view item;
}

getItem function  

This function returns the object at a special position. In the Android Java prototype of this function, the return value is Object, which means any type of Java object. But for C++, we must be return a clear.

C++
static VS_INT32 SRPAPI MyAdapter_getItem(void *Object,VS_INT32 Position)
{
    return Position;
}

Create a jni directory under the project, copy the header files of Wrapandroid and CLE into the jni directory. Create new files named code.cpp and Android.mk, edit Android.mk as follows:

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

Write native code in Android, and we should compile the source file into a share library. The share library exposes two functions:

C++
VS_BOOL StarCoreService_Init(class ClassOfStarCore *starcore)
void StarCoreService_Term(class ClassOfStarCore *starcore)

The first function will be called when the library is loaded, which can be used to do some initializing. StarCoreService_Term will be called before the share library is unloaded. The code of code.cpp is listed below. We will see this in detail:

C++
#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;
}

Screenshot

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)