Spinner、EditText、Button to DatePicker など、さまざまな種類のビューを持つ ListView があります。
さまざまな要素を区別するために、BaseAdapter のタグをこの ListView に追加しました。
フィールドのテキストを取得するには、メソッド findViewWithTag(Object tag) を使用します。
私はすでに Sony タブレット (Android 4.0.3) でアプリケーションをテストしており、メソッドは問題なくテキストを返します。
この問題は、Acer Liquid Duo z110 (Android 2.3.3) でテストしたときに始まりました。私は次のようなことをします:
Spinner spinner = (Spinner) listView.findViewWithTag("Item" + i);
// get value for the type
value = spinner.getSelectedItem().toString(); //Value is a string
行に NullPointerException が表示されますvalue=/*...*/
。
これは、ListView の要素を呼び出す BaseAdapter とダイアログです。
引用符
public class AttributeListAdapter extends BaseAdapter implements Serializable {
private static final long serialVersionUID = 1L;
private FeatureSet featureSet;
Field[] fields;
FeatureType[] types;
String typeIdFieldName;
private Context context;
LayoutInflater lInflator;
int[] editableFieldIndexes;
String[] typeNames;
HashMap<String, FeatureType> typeMap;
AttributeItem[] items;
private DateFormat formatter = DateFormat.getDateTimeInstance(
DateFormat.SHORT, DateFormat.SHORT);
/**
* Constructor
*/
public AttributeListAdapter(Context context, Field[] fields,
FeatureType[] types, String typeIdFieldName) {
this.setContext(context);
this.lInflator = LayoutInflater.from(context);
this.fields = fields;
this.types = types;
this.typeIdFieldName = typeIdFieldName;
// this.fieldsTemplateMap = createInitialFieldTemplateMap(this.fields);
// parseTypes();
// Setup processed variables
this.editableFieldIndexes = FeatureLayerUtils
.createArrayOfFieldIndexes(this.fields);
this.typeNames = FeatureLayerUtils.createTypeNameArray(this.types);
this.typeMap = FeatureLayerUtils.createTypeMapByValue(this.types);
// register dataset observer to track when the underlying data is
// changed
this.registerDataSetObserver(new DataSetObserver() {
public void onChanged() {
// clear the array of attribute items
AttributeListAdapter.this.items = new AttributeItem[AttributeListAdapter.this.editableFieldIndexes.length];
}
});
}
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
/**
* Implemented method from BaseAdapter class
*/
public int getCount() {
return this.editableFieldIndexes.length;
}
/**
* Implemented method from BaseAdapter class. This method returns the actual
* data associated with a row in the list. In this case we return the field
* along with the field value as a custom object. We subsequently add the
* View which displays the value to this object so we can retrieve it when
* applying edits.
*/
public Object getItem(int position) {
// get field associated with the position from the editableFieldIndexes
// array created at startup
int fieldIndex = this.editableFieldIndexes[position];
AttributeItem row = null;
// check to see if we have already created an attribute item if not
// create
// one
if (items[position] == null) {
// create new Attribute item for persisting the data for subsequent
// events
row = new AttributeItem();
row.setField(this.fields[fieldIndex]);
Object value = this.getFeatureSet().getGraphics()[0]
.getAttributeValue(fields[fieldIndex].getName());
row.setValue(value);
items[position] = row;
} else {
// reuse existing item to ensure View instance is kept.
row = items[position];
}
return row;
}
/**
* Implemented method from BaseAdapter class
*/
public long getItemId(int position) {
return position;
}
/**
* Implemented method from BaseAdapter class. This is the main method for
* returning a View which corresponds to a row in the list. This calls the
* getItem() method to get the data. It is called multiple times by the
* ListView and may be improved on by saving the previous result.
*/
public View getView(int position, View convertView, ViewGroup parent) {
View container = null;
AttributeItem item = (AttributeItem) getItem(position);
// check field type
// TODO if you want to support domains, add checks here and use the
// createSpinnerViewFromArray to create spinners
if (item.getField().getName().equals(this.typeIdFieldName)) {
// This is the featurelayers type field
container = lInflator.inflate(R.layout.item_spinner, null);
// get the types name for this feature from the available values
String typeStringValue = (item.getValue() != null) ? this.typeMap
.get(item.getValue().toString()).getName() : typeNames[2];
createSpinnerViewFromArray(position, container, item.getField(),
typeStringValue, this.typeNames);
// TODO set listener to change types associated domain fields if
// required
} else if (item.getField().getName().equals("etat_cana")) {
container = lInflator.inflate(R.layout.item_spinner, null);
String[] etatCana = { "", "Oxydée", "Désagrégée", "Autre",
"Pas d'observation" };
String typeStringValue = (item.getValue() != null) ? item
.getValue().toString() : etatCana[0];
createSpinnerViewFromArray(position, container, item.getField(),
typeStringValue, etatCana);
} else if (item.getField().getName().equals("alerte")) {
container = lInflator.inflate(R.layout.item_spinner, null);
String[] alerte = { "", "Oui", "Non" };
String typeStringValue = (item.getValue() != null) ? item
.getValue().toString() : alerte[0];
createSpinnerViewFromArray(position, container, item.getField(),
typeStringValue, alerte);
} else if (FieldType.determineFieldType(item.getField()) == FieldType.DATE) {
// create date picker for date fields
container = lInflator.inflate(R.layout.item_date, null);
long date = Long.parseLong((item.getValue() != null) ? item
.getValue().toString() : ""
+ Calendar.getInstance().getTimeInMillis());
createDateButtonFromLongValue(position, container, item.getField(),
date);
} else {
// create number and text fields
// View object for saving in the AttrbuteItem once it has been set
// up, for
// accessing later when we apply edits.
if (FieldType.determineFieldType(item.getField()) == FieldType.STRING) {
// get the string specific layout
container = lInflator.inflate(R.layout.item_text, null);
createAttributeRow(position, container, item.getField(),
item.getValue());
} else if (FieldType.determineFieldType(item.getField()) == FieldType.NUMBER) {
// get the number specific layout
container = lInflator.inflate(R.layout.item_number, null);
createAttributeRow(position, container, item.getField(),
item.getValue());
} else if (FieldType.determineFieldType(item.getField()) == FieldType.DECIMAL) {
// get the decimal specific layout
container = lInflator.inflate(R.layout.item_decimal, null);
createAttributeRow(position, container, item.getField(),
item.getValue());
}
}
container.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.i("AttributeListAdapter", "container clicked");
}
});
return container;
}
/**
* Sets the FeatureSet, called by the activity when a new queryResult is
* returned
*
* @param featureSet
*/
public void setFeatureSet(FeatureSet featureSet) {
this.featureSet = featureSet;
}
/**
* Helper method to create a spinner for a field and insert it into the View
* container. This uses, the String[] to create the list, and selects the
* value that is passed in from the list (the features value). Can be used
* for domains as well as types.
*
* @param position
*/
Spinner createSpinnerViewFromArray(int position, View container,
Field field, Object value, String[] values) {
TextView fieldAlias = (TextView) container
.findViewById(R.id.field_alias_txt);
Spinner spinner = (Spinner) container
.findViewById(R.id.field_value_spinner);
spinner.setTag("Item" + position);
fieldAlias.setText(field.getAlias());
spinner.setPrompt(field.getAlias());
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(
this.getContext(), android.R.layout.simple_spinner_item, values);
spinnerAdapter
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(spinnerAdapter);
// set current selection based on the value passed in
spinner.setSelection(spinnerAdapter.getPosition(value.toString()));
return spinner;
}
/**
* Helper method to create a date button, with appropriate onClick and
* onDateSet listeners to handle dates as a long (milliseconds since 1970),
* it uses the locale and presents a button with the date and time in short
* format.
*
* @param position
*/
Button createDateButtonFromLongValue(int position, View container,
Field field, long date) {
TextView fieldAlias = (TextView) container
.findViewById(R.id.field_alias_txt);
Button dateButton = (Button) container
.findViewById(R.id.field_date_btn);
dateButton.setTag("Item" + position);
fieldAlias.setText(field.getAlias());
Calendar c = Calendar.getInstance();
c.setTimeInMillis(date);
dateButton.setText(getFormatter().format(c.getTime()));
addListenersToDatebutton(dateButton);
return dateButton;
}
/**
* Helper method to add the field alias and the fields value into columns of
* a view using standard id names. If the field has a length set, then this
* is used to constrain the EditText's allowable characters. No validation
* is applied here, it is assumed that the container has this set already
* (in XML).
*
* @param position
*/
View createAttributeRow(int position, View container, Field field,
Object value) {
TextView fieldAlias = (TextView) container
.findViewById(R.id.field_alias_txt);
EditText fieldValue = (EditText) container
.findViewById(R.id.field_value_txt);
fieldValue.setTag("Item" + position);
fieldAlias.setText(field.getAlias());
// set the length of the text field and its value
if (field.getLength() > 0) {
InputFilter.LengthFilter filter = new InputFilter.LengthFilter(
field.getLength());
fieldValue.setFilters(new InputFilter[] { filter });
}
if (value != null) {
fieldValue.setText(value.toString(), BufferType.EDITABLE);
} else {
fieldValue.setText("", BufferType.EDITABLE);
}
return fieldValue;
}
/**
* Helper method to create the date button and its associated events
*/
void addListenersToDatebutton(Button dateButton) {
// create new onDateSetLisetener with the button associated with it
final ListOnDateSetListener listener = new ListOnDateSetListener(
dateButton);
// add a click listener to the button
dateButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// if its a date, get the milliseconds value
Calendar c = Calendar.getInstance();
getFormatter().setCalendar(c);
try {
// parse to a double
Button button = (Button) v;
c.setTime(getFormatter().parse(button.getText().toString()));
} catch (ParseException e) {
// do nothing as should parse
}
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY_OF_MONTH);
// show date picker with date set to the items value (hence
// built
// outside of onCreateDialog)
// TODO implement time picker if required, this picker only
// supports
// date and therefore showing the dialog will cause a change in
// the time
// value for the field
DatePickerDialog dialog = new DatePickerDialog(getContext(),
listener, year, month, day);
dialog.show();
}
});
}
ダイアログ
public class FeatureEditor extends Dialog {
private static String TAG = "FeatureEditor";
private static Context context;
/**
* Details of the feature layer "Evenements"
*/
private AttributeListAdapter listAdapter;
/**
* List layout of the edit feature dialog
*/
private View listLayout;
/**
* Layout of the edit feature dialog
*/
private ListView listView;
private ArcGISFeatureLayer editLayer;
private Activity aep;
private ArcGISDynamicMapServiceLayer aep41MapServer;
/**
* Constructor with the required parameters
* @param aep Activity where the dialog is shown
* @param edit Feature Layer where the edits are made
* @param aep41MapServer Dynamic Layer where the edits will be seen
* @param listAdapter The adapter with the attributes to edit
*/
public FeatureEditor(Activity aep, ArcGISFeatureLayer edit,
ArcGISDynamicMapServiceLayer aep41MapServer,
AttributeListAdapter listAdapter) {
super(aep);
this.aep = aep;
this.editLayer = edit;
this.aep41MapServer = aep41MapServer;
context = aep.getApplicationContext();
this.listAdapter = listAdapter;
listLayout = LayoutInflater.from(context).inflate(R.layout.list_layout,
null);
listView = (ListView) listLayout.findViewById(R.id.list_view);
listView.setAdapter(listAdapter);
setContentView(listLayout);
setTitle("Editeur d'Evenements");
Button apply = (Button) listLayout
.findViewById(R.id.EditFeatureApplyBt);
apply.setOnClickListener(applyEditButton());
Button cancel = (Button) listLayout
.findViewById(R.id.EditFeatureCancelBt);
cancel.setOnClickListener(cancelEditButton());
}
/**
* Action performed when the cancel button in the Edit Dialog is pressed
*
* @return OnClickListener
*/
private View.OnClickListener cancelEditButton() {
// TODO Auto-generated method stub
return new View.OnClickListener() {
public void onClick(View v) {
dismiss();
}
};
}
/**
* Action performed when the apply button in the Edit Dialog is pressed
*
* @return OnClickListener
*/
private View.OnClickListener applyEditButton() {
return new View.OnClickListener() {
public void onClick(View v) {
boolean isTypeField = false;
boolean hasEdits = false;
Map<String, Object> attrs = new HashMap<String, Object>();
// loop through each attribute and set the new values if they
// have
// changed
for (int i = 0; i < listAdapter.getCount(); i++) {
AttributeItem item = (AttributeItem) listAdapter.getItem(i);
String value = "";
if (item.getField().getName()
.equals(editLayer.getTypeIdField())) {
// drop down spinner
Spinner spinner = (Spinner) listView
.findViewWithTag("Item" + i);
// get value for the type
String typeName = spinner.getSelectedItem().toString();
value = FeatureLayerUtils.returnTypeIdFromTypeName(
editLayer.getTypes(), typeName);
// update map layer as for this featurelayer the
// type change will
// change the features symbol.
isTypeField = true;
} else if (item.getField().getName().equals("etat_cana")) {
// DropDown to Pipeline state
Spinner spinner = (Spinner) listView
.findViewWithTag("Item" + i);
// get value for the type
value = spinner.getSelectedItem().toString();
} else if (item.getField().getName().equals("alerte")) {
// DropDown to Pipeline state
Spinner spinner = (Spinner) listView
.findViewWithTag("Item" + i);
// get value for the type
value = spinner.getSelectedItem().toString();
} else if (FieldType.determineFieldType(item.getField()) == FieldType.DATE) {
// date
Button dateButton = (Button) listView
.findViewWithTag("Item" + i);
value = dateButton.getText().toString();
} else {
// edit text
EditText editText = (EditText) listView
.findViewWithTag("Item" + i);
value = editText.getText().toString();
}
// try to set the attribute value on the graphic and see
// if it has
// been changed
boolean hasChanged = FeatureLayerUtils.setAttribute(attrs,
listAdapter.getFeatureSet().getGraphics()[0],
item.getField(), value, listAdapter.getFormatter());
// if a value has for this field, log this and set the
// hasEdits
// boolean to true
if (hasChanged) {
Log.d(TAG, "Change found for field="
+ item.getField().getName() + " value = "
+ value + " applyEdits() will be called");
hasEdits = true;
}
// check if this was a type field, if so set boolean
// back to false
// for next field
if (isTypeField) {
isTypeField = false;
}
}
// check there have been some edits before applying the changes
if (hasEdits) {
// set objectID field value from graphic held in the
// featureset
attrs.put(editLayer.getObjectIdField(), listAdapter
.getFeatureSet().getGraphics()[0]
.getAttributeValue(editLayer.getObjectIdField()));
final Graphic newGraphic = new Graphic(null, null, attrs,
null);
editLayer.applyEdits(null, null,
new Graphic[] { newGraphic },
new CallbackListener<FeatureEditResult[][]>() {
public void onError(Throwable arg0) {
}
public void onCallback(
final FeatureEditResult[][] arg0) {
aep.runOnUiThread(new Runnable() {
public void run() {
aep41MapServer.refresh();
if (arg0[2] != null
&& arg0[2][0] != null
&& arg0[2][0].isSuccess()) {
Log.i(TAG, "Feature edited");
Log.i(TAG,
arg0[2][0].toString());
aep41MapServer.refresh();
Toast.makeText(context, "Edition d'événement réussi", Toast.LENGTH_SHORT).show();
} else {
Log.e(TAG,
"Error editing feature");
Toast.makeText(context, "Edition d'evenement échoe", Toast.LENGTH_SHORT).show();
}
}
});
}
});
}
editLayer.clearSelection();
dismiss();
}
};
}
}
BaseAdapter を使用した ListView は、この異なるバージョンの Android で同じように機能しますか? 私は何か間違ったことをしていますか?
前述のとおり、アプリは Android 4.0.3 ではエラーなく動作しますが、Android 2.3.3 ではエラーが発生しません。
助けてくれてありがとう ;)
受信した logCat は次のとおりです。
E/CellLocation( 4779): create GsmCellLocation
E/TelephonyManager( 4779): getDefaultSim is sim1
E/CellLocation( 4779): create GsmCellLocation
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/CellLocation( 166): create GsmCellLocation
E/CellLocation( 166): create GsmCellLocation
E/CellLocation( 166): create GsmCellLocation
E/CellLocation( 4823): create GsmCellLocation
E/TelephonyManager( 4823): getDefaultSim is sim1
E/CellLocation( 4823): create GsmCellLocation
E/TelephonyManager( 4823): getDefaultSim is sim1
E/TelephonyManager( 4823): getDefaultSim is sim1
E/TelephonyManager( 4823): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4823): getDefaultSim is sim1
E/TelephonyManager( 4823): getDefaultSim is sim1
E/TelephonyManager( 4823): getDefaultSim is sim1
E/Resources( 6063): sPreloadedDrawables size is 0, reload preloaded resources.
E/dalvikvm( 6063): [DVM] mmap return base = 47c58000
E/dalvikvm( 6063): [DVM] mmap return base = 48837000
E/dalvikvm( 6063): [DVM] mmap return base = 48a3f000
E/TelephonyManager( 4779): getDefaultSim is sim1
E/CellLocation( 4779): create GsmCellLocation
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/dalvikvm( 6063): [DVM] mmap return base = 48733000
E/TelephonyManager( 4779): getDefaultSim is sim1
E/CellLocation( 4779): create GsmCellLocation
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/wpa_supplicant( 236): Ongoing Scan action...
E/TelephonyManager( 4779): getDefaultSim is sim1
E/CellLocation( 4779): create GsmCellLocation
E/dalvikvm( 4779): [DVM] mmap return base = 476eb000
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/dalvikvm( 6063): [DVM] mmap return base = 48e49000
E/TelephonyManager( 4779): getDefaultSim is sim1
E/dalvikvm( 6063): [DVM] mmap return base = 4915a000
E/dalvikvm( 6063): [DVM] mmap return base = 4925a000
E/dalvikvm( 6063): [DVM] mmap return base = 49d0a000
E/dalvikvm( 4009): [DVM] mmap return base = 462ab000
E/dalvikvm( 6063): [DVM] mmap return base = 49d0a000
--------- beginning of /dev/log/system
E/AndroidRuntime( 6063): FATAL EXCEPTION: main
E/AndroidRuntime( 6063): java.lang.NullPointerException
E/AndroidRuntime( 6063): at com.AEP41.main.CustomDialogs.FeatureEditor$2.onClick(FeatureEditor.java:158)
E/AndroidRuntime( 6063): at android.view.View.performClick(View.java:2538)
E/AndroidRuntime( 6063): at android.view.View$PerformClick.run(View.java:9135)
E/AndroidRuntime( 6063): at android.os.Handler.handleCallback(Handler.java:618)
E/AndroidRuntime( 6063): at android.os.Handler.dispatchMessage(Handler.java:123)
E/AndroidRuntime( 6063): at android.os.Looper.loop(SourceFile:351)
E/AndroidRuntime( 6063): at android.app.ActivityThread.main(ActivityThread.java:3826)
E/AndroidRuntime( 6063): at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 6063): at java.lang.reflect.Method.invoke(Method.java:538)
E/AndroidRuntime( 6063): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:969)
E/AndroidRuntime( 6063): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:727)
E/AndroidRuntime( 6063): at dalvik.system.NativeStart.main(Native Method)
E/dalvikvm( 166): [DVM] mmap return base = 4c13a000
E/AEE/LIBAEE( 166): read_cmdline:com.AEP41.main
E/AEE/AED ( 80): jni/../aed/aed_trace.c, 158, No such file or directory
E/dalvikvm( 5446): [DVM] mmap return base = 45a54000