2011年12月9日 星期五 | By: 雞米

[Android] Activity之間如何透過Intent傳遞複雜結構的data

(How to add complex data to the intent object)


Application是由許多Activities所組成,不同Activity之間是藉由Intent物件來進行啟動/關閉,Intent物件可以帶入data傳遞給它所喚起的Activity,它可能是上個Activity執行後的結果。

在Intent object加入data最常見的方法即為putExtra(),它提供了多種overload方法,包含各種primitive data type以及其Array資料型態,另外它也支援實作Serializable與Parcelable兩種介面的資料型態。

當遇到需要傳遞較複雜的資料型態(自定義且putExtra沒有對應的多載函式)給即將開啟的Activity,例如:Custom list view's data source就常使用HashMap、ArrayList等資料型態的組合,這些都是實作Serializable常見的類別,此時就可以採用putExtra(String name, Serializable value)將資料帶入Intent object,接受端Activity再透過getSerializableExtra(String name)取得自定義的Serializable data,藉此達到傳遞複雜資料的目的。

以下將示範透過Intent將自定義資料型態ArrayList< HashMap>傳遞給Custom ListView作為其Adapter之資料來源。

程式碼說明

一、建立負責activate ListActivity之Activity
package com.intentdemo;

import java.util.ArrayList;
import java.util.HashMap;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;

public class IntentDemoActivity extends Activity {
    
private int[] icons={
 R.drawable.icon1,R.drawable.icon2,
 R.drawable.icon3,R.drawable.icon4,
 R.drawable.icon5,R.drawable.icon6
};
 
private String[] names={
 "role1","role2",
 "role3","role4",
 "role5","role6"
};
 
private String[] tels={
 "000-11111","999-19191",
 "000-22222","919-49791",
 "101-33333","929-29391"
};
 
@Override
public void onCreate(Bundle savedInstanceState){
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);
}

}
IntentDemoActivity.java
二、宣告自定義資料型態
private ArrayList< HashMap< String,Object>> getListData(){
 ArrayList dataSet = new ArrayList();
 for(int i=0;i< 6;i++){
 HashMap< String,Object> data = new HashMap< String,Object>(); 
 data.put("icon", icons[i]);
 data.put("name", names[i]);
 data.put("tel", tels[i]);
 dataSet.add(data);
 }
 return dataSet;
}
三、按鈕click事件觸發Intent並附帶自定義data
public void onIntentButtonClick(View v){
 Intent intent=new Intent();
 intent.putExtra("listData", getListData());
 intent.setClass(this, RoleListActivity.class);
 startActivity(intent);
}
四、Custom ListActivity透過getSerializableExtra()接收data並將其設為ListView Adapter資料來源
package com.intentdemo;

import java.util.ArrayList;
import java.util.HashMap;

import android.app.ListActivity;
import android.os.Bundle;

public class RoleListActivity extends ListActivity {
 
@Override
public void onCreate(Bundle savedInstanceState){
 super.onCreate(savedInstanceState);
 setContentView(R.layout.role_list);
  
 //get intent data
 ArrayList< HashMap< String,Object>> data=
 (ArrayList< HashMap< String, Object>>)getIntent()
 .getSerializableExtra("listData");
 this.setListAdapter(new ListAdapter(this,data));
}
}
RoleListActivity.java
package com.intentdemo;

import java.util.ArrayList;
import java.util.HashMap;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class ListAdapter extends BaseAdapter {
 
 private ArrayList< hashmap> data;
 private LayoutInflater inflater;
 
 public ListAdapter(Context c,ArrayList< HashMap< String,Object>> d){
 inflater=LayoutInflater.from(c);
 data=d;
 }
 
 @Override
 public int getCount() {
  return data.size();
 }

 @Override
 public Object getItem(int index) {
  return data.get(index);
 }

 @Override
 public long getItemId(int index) {
  return index;
 }

 @Override
 public View getView(int i, View v, ViewGroup p) {
  ViewHolder holder;
  if(v==null){
   v=inflater.inflate(R.layout.role_list_row, null);
   holder=new ViewHolder();
   holder.icon=(ImageView)v.findViewById(R.id.icon);
   holder.nameTxt=(TextView)v.findViewById(R.id.name);
   holder.telTxt=(TextView)v.findViewById(R.id.tel);
   v.setTag(holder);
   }else{
   holder=(ViewHolder)v.getTag();
   }
  
 holder.icon.setImageResource((Integer)data.get(i).get("icon"));
 holder.nameTxt.setText((String)data.get(i).get("name"));
 holder.telTxt.setText((String)data.get(i).get("tel"));
  
   return v;
  }
 
  static class ViewHolder{
  ImageView icon;
  TextView nameTxt;
  TextView telTxt;
  }
}
ListAdapter.java
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical" >
  <ListView
  android:id="@android:id/list"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
/>
</LinearLayout>
role_list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="100dp"
 android:orientation="horizontal" >
 <ImageView 
  android:id="@+id/icon"
  android:layout_width="90dp"
  android:layout_height="90dp"
  android:padding="5dp"    
 />

 <LinearLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:gravity="center_vertical"
  android:orientation="vertical"
 >
 <TextView 
  android:id="@+id/name"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:gravity="center"
  android:textSize="20sp"
 />
 <TextView 
  android:id="@+id/tel"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:gravity="center"
  android:textSize="20sp"
 />    
 </LinearLayout>
</LinearLayout>
role_list_row.xml



6 意見:

Unknown 提到...

您好 ,板大
我遇到一個問題

Diy diy = new Diy();//自定義的物件
ArrayList dataSet = new ArrayList();
HashMap data = new HashMap();
data.put("物件名稱", diy);
dataSet.add(data);

Intent intent = new Intent();
intent.setClass(A.this, B.class);
intent.putExtra("傳送名稱", dataSet);
startActivityForResult(intent, 0); // 這行會出錯

不曉得是錯在哪?
望板大指點

雞米 提到...

請問錯誤訊息是?
startActivityForResult函式的第二個參數是什麼型態的變數?

Unknown 提到...

08-22 13:23:17.124: D/libEGL(31448): loaded /system/lib/egl/libGLES_android.so
08-22 13:23:17.134: D/libEGL(31448): loaded /system/lib/egl/libEGL_adreno200.so
08-22 13:23:17.134: D/libEGL(31448): loaded /system/lib/egl/libGLESv1_CM_adreno200.so
08-22 13:23:17.144: D/libEGL(31448): loaded /system/lib/egl/libGLESv2_adreno200.so
08-22 13:23:17.184: D/OpenGLRenderer(31448): Enabling debug mode 0
08-22 13:23:34.270: D/libEGL(31543): loaded /system/lib/egl/libGLES_android.so
08-22 13:23:34.290: D/libEGL(31543): loaded /system/lib/egl/libEGL_adreno200.so
08-22 13:23:34.290: D/libEGL(31543): loaded /system/lib/egl/libGLESv1_CM_adreno200.so
08-22 13:23:34.300: D/libEGL(31543): loaded /system/lib/egl/libGLESv2_adreno200.so
08-22 13:23:34.370: D/OpenGLRenderer(31543): Enabling debug mode 0
08-22 13:24:03.989: D/AndroidRuntime(31543): Shutting down VM
08-22 13:24:03.989: W/dalvikvm(31543): threadid=1: thread exiting with uncaught exception (group=0x40aa9228)
08-22 13:24:04.009: E/AndroidRuntime(31543): FATAL EXCEPTION: main
08-22 13:24:04.009: E/AndroidRuntime(31543): java.lang.RuntimeException: Parcel: unable to marshal value com.example.test.Diy@40d83580
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.os.Parcel.writeValue(Parcel.java:1137)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.os.Parcel.writeMapInternal(Parcel.java:493)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.os.Parcel.writeMap(Parcel.java:477)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.os.Parcel.writeValue(Parcel.java:1068)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.os.Parcel.writeList(Parcel.java:524)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.os.Parcel.writeValue(Parcel.java:1097)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.os.Parcel.writeMapInternal(Parcel.java:493)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.os.Bundle.writeToParcel(Bundle.java:1612)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.os.Parcel.writeBundle(Parcel.java:507)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.content.Intent.writeToParcel(Intent.java:6464)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.app.ActivityManagerProxy.startActivity(ActivityManagerNative.java:1673)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.app.Instrumentation.execStartActivity(Instrumentation.java:1505)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.app.Activity.startActivityForResult(Activity.java:3251)
08-22 13:24:04.009: E/AndroidRuntime(31543): at com.example.test.MainActivity$1.onClick(MainActivity.java:32)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.view.View.performClick(View.java:3549)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.view.View$PerformClick.run(View.java:14400)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.os.Handler.handleCallback(Handler.java:605)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.os.Handler.dispatchMessage(Handler.java:92)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.os.Looper.loop(Looper.java:154)
08-22 13:24:04.009: E/AndroidRuntime(31543): at android.app.ActivityThread.main(ActivityThread.java:4945)
08-22 13:24:04.009: E/AndroidRuntime(31543): at java.lang.reflect.Method.invokeNative(Native Method)
08-22 13:24:04.009: E/AndroidRuntime(31543): at java.lang.reflect.Method.invoke(Method.java:511)
08-22 13:24:04.009: E/AndroidRuntime(31543): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
08-22 13:24:04.009: E/AndroidRuntime(31543): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
08-22 13:24:04.009: E/AndroidRuntime(31543): at dalvik.system.NativeStart.main(Native Method)

Unknown 提到...

startActivityForResult(Intent,int);主要是可以回傳資料用
如果沒有要回傳就用startActivity(Intent)即可,不過還是會出錯
原始碼
http://dl.dropbox.com/u/35125399/test.7z

雞米 提到...

我大概看了一下程式碼,出錯的地方不是在於傳的參數,應該是你要回傳值的Activity沒有宣告在Android.manifest裡,以上提供給你參考

黃文錦 提到...

You should make your com.example.test.Diy class implement Parcelable.

張貼留言