/* * GeoSolutions - MapstoreMobile - GeoSpatial Framework on Android based devices * Copyright (C) 2014 - 2016 GeoSolutions (www.geo-solutions.it) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package it.geosolutions.geocollect.android.core.form.utils; import it.geosolutions.android.map.control.CoordinateControl; import it.geosolutions.android.map.control.LocationControl; import it.geosolutions.android.map.control.MarkerControl; import it.geosolutions.android.map.dto.MarkerDTO; import it.geosolutions.android.map.model.Layer; import it.geosolutions.android.map.model.MSMMap; import it.geosolutions.android.map.overlay.MarkerOverlay; import it.geosolutions.android.map.overlay.items.DescribedMarker; import it.geosolutions.android.map.overlay.managers.MultiSourceOverlayManager; import it.geosolutions.android.map.utils.MapFilesProvider; import it.geosolutions.android.map.utils.SpatialDbUtils; import it.geosolutions.android.map.view.AdvancedMapView; import it.geosolutions.android.map.wfs.geojson.feature.Feature; import it.geosolutions.geocollect.android.app.BuildConfig; import it.geosolutions.geocollect.android.app.R; import it.geosolutions.geocollect.android.core.GeoCollectApplication; import it.geosolutions.geocollect.android.core.form.FormEditActivity; import it.geosolutions.geocollect.android.core.form.ViewPagerAwareMarkerControl; import it.geosolutions.geocollect.android.core.mission.MissionFeature; import it.geosolutions.geocollect.android.core.mission.utils.LocationProvider; import it.geosolutions.geocollect.android.core.mission.utils.MissionUtils; import it.geosolutions.geocollect.android.core.mission.utils.LocationProvider.LocationResultCallback; import it.geosolutions.geocollect.android.core.widgets.DatePicker; import it.geosolutions.geocollect.android.core.widgets.UILImageAdapter; import it.geosolutions.geocollect.model.config.MissionTemplate; import it.geosolutions.geocollect.model.viewmodel.Field; import java.io.File; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import org.mapsforge.android.maps.MapView; import org.mapsforge.android.maps.mapgenerator.mbtiles.MbTilesDatabaseRenderer; import org.mapsforge.core.model.GeoPoint; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Typeface; import android.location.Location; import android.preference.PreferenceManager; import android.text.InputType; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.CheckBox; import android.widget.EditText; import android.widget.GridView; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.AdapterView.OnItemClickListener; import android.widget.LinearLayout.LayoutParams; import android.widget.SimpleAdapter; import android.widget.Spinner; import android.widget.SpinnerAdapter; import android.widget.TextView; import com.actionbarsherlock.app.SherlockFragmentActivity; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.vividsolutions.jts.geom.Point; /** * @author Lorenzo Natali (lorenzo.natali@geo-solutions.it) * * @author Robert Oehler * * Utility Class to build forms adapted for MissionFeatures */ public class MissionFeatureFormBuilder { /** * Tag for logging */ public static String TAG = "MissionFeatureFormBuilder"; /** * Default zoom value */ public static int DEFAULT_ZOOM_VALUE = 18; // set a static id private static int sId = 0; private static int id() { return sId++; } /** * Creates and add the form fields to the layout passed * * @param context * the context * @param mFormView * the Layout * @param fields * list of fields * @param feature * the according missionFeature */ public static void buildForm(Context context, LinearLayout mFormView, List<Field> fields, MissionFeature feature) { if (fields == null) { return; } for (Field f : fields) { //the null field will be ignored if(f == null )continue; if (f.xtype == null) { //textfield as default addTextField(f, mFormView, context,feature); } else { // switch witch widget create switch (f.xtype) { case textfield: addTextField(f, mFormView, context,feature); break; case textarea: addTextField(f, mFormView, context,feature); break; case datefield: addDatePicker(f, mFormView, context,feature); break; case checkbox: addCheckBox(f,mFormView,context,feature); break; case spinner: addSpinner(f,mFormView,context,feature); break; case label: addLabel(f,mFormView,context,feature); break; case photo: addPhotoGrid(f, mFormView, context, feature); break; case mapViewPoint: addMapViewPoint(f,mFormView,context,feature); break; default: //textfield as default addTextField(f, mFormView, context,feature); } } } } /** * Create an Header with GridView * @param f * @param mFormView * @param context * @param mission */ private static void addPhotoGrid(Field field, LinearLayout mFormView, Context context, Feature feature) { // TODO: enable label? //TextView tvLabel = getLabelForField(field, context); // TODO: Null check on this line GridView photoView = (GridView) ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.ac_image_grid, mFormView, false);; photoView.setLayoutParams(getTextDefaultParams(field, true)); //photoView.setTag(field.fieldId); // TODO: useless, the photoView must be used to fetch from a folder derived from the mission id // TODO: use a non-string tag photoView.setTag("__photo__"); //String[] stringUrls = FormUtils.getPhotoUriStrings(mission.getOrigin().id); DisplayImageOptions options = new DisplayImageOptions.Builder() .showImageOnLoading(it.geosolutions.geocollect.android.app.R.drawable.ic_launcher) .showImageForEmptyUri(it.geosolutions.geocollect.android.app.R.drawable.ic_empty) .showImageOnFail(it.geosolutions.geocollect.android.app.R.drawable.ic_error) .resetViewBeforeLoading(false) .cacheInMemory(false) .cacheOnDisk(false) .considerExifParams(true) .bitmapConfig(Bitmap.Config.RGB_565) .imageScaleType(ImageScaleType.EXACTLY) .build(); //*************************** photoView.setAdapter(new UILImageAdapter(context, MissionUtils.getFeatureGCID(feature), options)); // enable ContexxtMenu on LongPress ((FormEditActivity) context).registerForContextMenu(photoView); photoView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //startImagePagerActivity(position); Log.v("ItemListener", "Clicked position "+position+" , id "+id+ ", ViewID "+view.getId()); // TODO: start full image show activity //view.showContextMenu(); } }); /* photoView.setOnItemLongClickListener(new OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { Log.v("ItemListener", "LONG Click on position "+position+" , id "+id+ ", ViewID "+view.getId()); // TODO: open contextual menu for deletion return true; } }); */ // TODO: enable label? //mFormView.addView(tvLabel); mFormView.addView(photoView); } /** * Creates a map view - when a valid geometry is available the map will be * centered on it - otherwise a location acquiring will be started which needs * the priorly checked activated location services - its result will update the map * @param f * @param mFormView * @param context * @param missionFeature */ private static void addMapViewPoint(final Field field, LinearLayout mFormView, final Context context, final MissionFeature feature) { TextView tvLabel = getLabelForField(field, context); AdvancedMapView mapView =null; ImageButton buttonLocation = null; if(context instanceof FormEditActivity){ View formView = ((FormEditActivity)context).getLayoutInflater().inflate(R.layout.form_mapview, null); mapView = (AdvancedMapView) formView.findViewById(R.id.advancedMapView); buttonLocation = (ImageButton) formView.findViewById(R.id.ButtonLocation); mFormView.addView(tvLabel); mapView.setTag(field.fieldId); mFormView.addView(formView); }else{ if(mapView==null){ mapView = new AdvancedMapView(context); mapView.setLayoutParams(getMapLayoutParams(field));//if not in layout mFormView.addView(tvLabel); //mFormView.addView(infoButton); mapView.setMinimumHeight(100);//it doesn't work, try a different method mapView.setMinimumWidth(100); mapView.setTag(field.fieldId); mFormView.addView(mapView); } } //setup overlay Manager final MultiSourceOverlayManager o = new MultiSourceOverlayManager(mapView); mapView.setOverlayManger(o); final MarkerOverlay m = new MarkerOverlay(); o.setMarkerOverlay(m); m.setProjection(mapView.getProjection()); // only from tag is supported GeoPoint geoPoint = null; boolean hasProvider = false; if(feature.geometry != null){ final Point p = feature.geometry.getCentroid(); //check if point was set to 0,0 if((!isZero(p.getX(),0.0001d)) && (!isZero(p.getY(),0.0001d))){ //if not, use it later when centering geoPoint = new GeoPoint(p.getY(), p.getX()); }else{ //get a better position hasProvider = acquirePosition(context, mapView, m, o); } }else{ //get a position hasProvider = acquirePosition(context, mapView, m, o); } o.setMarkerOverlay(m); //check editable Boolean editable = (Boolean)getAttributeWithDefault(field,"editable",true); //check disablePan Boolean disablePan = (Boolean)getAttributeWithDefault(field,"disablePan",false); //add marker control MarkerControl mc; if(context instanceof FormEditActivity) mc = new ViewPagerAwareMarkerControl(mapView,editable, ((FormEditActivity)context).mViewPager); else mc = new MarkerControl(mapView,editable); //mc.setInfoButton(infoButton); mapView.addControl(mc); if(editable){ mc.setMode(MarkerControl.MODE_EDIT); } File mapFile = MapFilesProvider.getBackgroundMapFile(); String filePath = PreferenceManager.getDefaultSharedPreferences(context).getString(MapView.MAPSFORGE_BACKGROUND_FILEPATH, null); int type = Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(context).getString(MapView.MAPSFORGE_BACKGROUND_RENDERER_TYPE, "0")); //if the map file was edited in the preferences if(filePath != null && type == 0){ //use it mapView.setMapFile(new File(filePath)); }else if(filePath != null && type == 1){ // Set a fake background, will not be rendered // TODO: Change this workaround when the library will support setting MBTILES files as actual backgrounds mapView.setMapFile((new File(MapFilesProvider.getBaseDirectoryFile(), "bg.map"))); // Set the MBTILES background renderer mapView.setRenderer(new MbTilesDatabaseRenderer(context, filePath), true); }else if (mapFile!=null) { Log.i(TAG,"setting background file"); mapView.setMapFile(mapFile); } else { Log.i(TAG,"unable to set background file"); } // Load map from mission if(context instanceof SherlockFragmentActivity){ MissionTemplate t = ((GeoCollectApplication) ((SherlockFragmentActivity)context).getApplication()).getTemplate(); MSMMap missionmap = SpatialDbUtils.mapFromDb(); ArrayList<String> bg_layers = null; try{ if(t.config != null && t.config.get(MissionTemplate.BG_LAYERS_KEY) != null){ if(t.config.get(MissionTemplate.BG_LAYERS_KEY) instanceof ArrayList<?>){ bg_layers = (ArrayList<String>) t.config.get(MissionTemplate.BG_LAYERS_KEY); } } }catch (ClassCastException cce){ if(BuildConfig.DEBUG){ Log.w(TAG, "backgroundLayers tag is not an ArrayList, ignoring"); } bg_layers = null; } // Use only the layers that are related to this Mission ArrayList<Layer> layersList = new ArrayList<Layer>(); Layer layer = null; for (Iterator<Layer> it = missionmap.layers.iterator(); it.hasNext();) { layer = it.next(); if (layer.getTitle().equals(t.schema_seg.localSourceStore) || layer.getTitle().equals(t.schema_sop.localFormStore) || layer.getTitle().equals(t.schema_seg.localSourceStore + MissionTemplate.NEW_NOTICE_SUFFIX) ) { layersList.add(layer); }else if (bg_layers != null && bg_layers.contains(layer.getTitle())){ // Adding in the head position, so the layer will // be on the last in the LayerSwitcher order layersList.add(0, layer); } } // Set the correct layers missionmap.layers = layersList; o.loadMap(missionmap); } //pannable mapView.setClickable(!disablePan); mapView.setBuiltInZoomControls(true); //set center and zoom level limits Integer b = DEFAULT_ZOOM_VALUE; Object zoomValue = getAttributeWithDefault( field, "zoom", DEFAULT_ZOOM_VALUE); if(zoomValue instanceof Integer){ b = (Integer) b; } mapView.getMapViewPosition().setZoomLevel(b.byteValue()); mapView.getMapZoomControls().setZoomLevelMax((byte) 30); mapView.getMapZoomControls().setZoomLevelMin((byte) 1); //setup tile cache mapView.getFileSystemTileCache().setPersistent(true); if(geoPoint != null){ centerMapAndSetMarker(context, geoPoint, mapView, m, o); }else if (!hasProvider && mapView.getMapRenderer() != null){ // Set a marker on the center of the map centerMapAndSetMarker(context, mapView.getMapRenderer().getStartPoint() , mapView, m, o); } if(context instanceof FormEditActivity){ //add coordinates control mapView.addControl(new CoordinateControl(mapView, true)); //add "location" control connected to the button LocationControl lc =new LocationControl(mapView); lc.setActivationButton(buttonLocation); mapView.addControl(lc); } } /** * start the location acquisition * @return false if no providers are available, true otherwise * @param context * @param mapView * @param m * @param o * @param field */ public static boolean acquirePosition(final Context context,final MapView mapView,final MarkerOverlay m,final MultiSourceOverlayManager o){ LocationResultCallback locationResult = new LocationResultCallback(){ @Override public void gotLocation(final Location location){ if(location != null){ centerMapAndSetMarker(context, new GeoPoint(location.getLatitude(), location.getLongitude()), mapView, m, o); } } }; LocationProvider lp = new LocationProvider(); boolean hasProvider = lp.getLocation(context, locationResult); if(!hasProvider){ if(BuildConfig.DEBUG){ Log.d(TAG, "No location provider found"); } } return hasProvider; } /** * center the mapView on the provided geopoint, add a marker and show it * @param context * @param geoPoint * @param mapView * @param m * @param o * @param field */ public static void centerMapAndSetMarker(Context context, final GeoPoint geoPoint, MapView mapView, MarkerOverlay m, MultiSourceOverlayManager o){ if(geoPoint == null || mapView == null || m == null || o == null){ return; } o.setMarkerVisible(); DescribedMarker marker = new MarkerDTO(geoPoint.latitude, geoPoint.longitude,MarkerDTO.PIN_BLUE).createMarker(context); m.getOverlayItems().add(marker); //mc.selectMarker(marker); mapView.getMapViewPosition().setCenter(geoPoint); } /** * check if a double is 0 which may fail for d == 0 * @param value * @param threshold * @return */ public static boolean isZero(double value, double threshold){ return value >= -threshold && value <= threshold; } /** * Create a not editable text field * @param f * @param mFormView * @param context * @param missionFeature */ private static void addLabel(Field field, LinearLayout mFormView,Context context, MissionFeature feature) { //set label TextView tvLabel = getLabelForField(field, context); //set text TextView text = new TextView(context); text.setLayoutParams(getTextDefaultParams(field, false)); if(field.lines!=null){ text.setSingleLine(false); text.setLines(field.lines); text.setMaxLines(field.lines); } // setting an unique id is important in order to save the state // (content) of this view across screen configuration changes //int type = getInputType(field); text.setId(id()); text.setTag(field.fieldId); //text.setInputType(type) if(feature.properties.get(field.fieldId) != null){ text.setText((String) feature.properties.get(field.fieldId)); } mFormView.addView(tvLabel); mFormView.addView(text); } /** * Creates a <Spinner> * @param field * @param mFormView * @param context * @param mission */ private static void addSpinner(Field field, LinearLayout mFormView, Context context, MissionFeature feature) { TextView tvLabel = getLabelForField(field, context); String[] allowed = getFieldAllowedFrom(field); SpinnerAdapter adapter = new SimpleAdapter( context, getFieldAllowedData(field), android.R.layout.simple_spinner_dropdown_item, allowed, new int[]{android.R.id.text1} ); Spinner spinner = new Spinner(context); //set initial value //THIS does nothing // if(feature.properties.get(field.fieldId) != null){ // String value = (String) feature.properties.get(field.fieldId); // List<HashMap<String, String>> maps = getFieldAllowedData(field); // if(value!=null && allowed != null && maps!=null){ // int valueIndex=-1; // for(int i = 0;i<maps.size();i++){ // if(value.equals(maps.get(i).get(allowed[0]))); // valueIndex = i; // } // if(valueIndex>=0){ // spinner.setSelection(valueIndex); // } // } // } LayoutParams lp = getTextDefaultParams(field, false); spinner.setLayoutParams(lp); // setting an unique id is important in order to save the state // (content) of this view across screen configuration changes spinner.setId(id()); spinner.setTag(field.fieldId); mFormView.addView(tvLabel); mFormView.addView(spinner); spinner.setAdapter(adapter); } private static TextView getLabelForField(Field field, Context context) { String label = field.label; TextView tvLabel = new TextView(context); tvLabel.setLayoutParams(getTextDefaultParams(field,true)); tvLabel.setText(label); String labelStyle = (String)field.getAttribute("labelStyle"); if(labelStyle !=null){ //TODO use style attribute }else{ //default style for field label tvLabel.setTypeface(null, Typeface.BOLD|Typeface.ITALIC); } //if label is missing set none if(label == null){ tvLabel.setVisibility(TextView.GONE); } return tvLabel; } /** * Provides the attribute of the map to use in the spinner * @param field * @return */ private static String[] getFieldAllowedFrom(Field field) { //TODO make it configurable return new String[]{"f1"}; } /** * Provides the Data for the spinner * @param field * @return */ public static List<HashMap<String,String>> getFieldAllowedData(Field field){ ArrayList<HashMap<String,String>> data = new ArrayList<HashMap<String,String>>(); if(field.options!=null){ for(String value : field.options){ HashMap<String,String> v1 =new HashMap<String,String>(); v1.put("f1", value); data.add(v1); } } return data; } /** * Creates a date picker * * @param f * @param mFormView * @param context * @param missionFeature */ private static void addDatePicker(Field field, LinearLayout mFormView, Context context, MissionFeature feature) { int type = getInputType(field); TextView tvLabel = getLabelForField(field, context); DatePicker editView = new DatePicker(context,null); LayoutParams lp = getTextDefaultParams(field, false); editView.setLayoutParams(lp); // setting an unique id is important in order to save the state // (content) of this view across screen configuration changes editView.setId(id()); editView.setTag(field.fieldId); editView.setInputType(type); mFormView.addView(tvLabel); mFormView.addView(editView); if(feature.properties.get(field.fieldId) != null){ String value = (String) feature.properties.get(field.fieldId); //get the value if(value !=null){ DateFormat df = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()); try{ Calendar c = Calendar.getInstance(); c.setTime(df.parse(value)); editView.setDate(c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DATE)); }catch(ParseException e){ Log.e("FORM_PARSER","unable to parse date:"+ value + "with format string" + field.format); } } }else{ //set today Calendar c = Calendar.getInstance(); c.setTime(new Date()); editView.setDate(c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DATE)); } } /** * Add an <EditText> multiline * * @param field * the field configuration * @param mFormView * the form view * @param context * the context */ public static void addTextArea(Field field, LinearLayout mFormView, Context context) { int type = getInputType(field); TextView tvLabel = getLabelForField(field, context); EditText editView = new EditText(context); editView.setLayoutParams(getTextDefaultParams(field, false)); editView.setSingleLine(false); editView.setLines(field.lines); // setting an unique id is important in order to save the state // (content) of this view across screen configuration changes editView.setId(id()); editView.setInputType(type); mFormView.addView(tvLabel); mFormView.addView(editView); } /** * Add a <EditText> single line * * @param label * @param type * @param context * @param missionFeature */ public static void addTextField(Field field, LinearLayout mFormView, Context context, MissionFeature feature) { int type = getInputType(field); TextView tvLabel = getLabelForField(field, context); EditText editView = null; //set autocomplete if options present if(field.options!=null){ editView = addAutoCompleteField(field,mFormView,context); }else{ editView = new EditText(context); } editView.setLayoutParams(getTextDefaultParams(field, false)); // setting an unique id is important in order to save the state // (content) of this view across screen configuration changes editView.setId(id()); editView.setTag(field.fieldId); editView.setInputType(type); if(feature.properties.get(field.fieldId) != null){ editView.setText((String) feature.properties.get(field.fieldId) ); } final String mandatoryTag = field.mandatory ? " ("+mFormView.getContext().getString(R.string.mandatory)+")" : ""; tvLabel.setText(tvLabel.getText()+mandatoryTag); mFormView.addView(tvLabel); mFormView.addView(editView); } /** * Add a <EditText> single line * * @param label * @param type * @param context */ public static AutoCompleteTextView addAutoCompleteField(Field field, LinearLayout mFormView, Context context) { AutoCompleteTextView editView = (AutoCompleteTextView) new AutoCompleteTextView(context); // Get the string array if(field.options !=null){ ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, android.R.layout.simple_dropdown_item_1line, field.options); editView.setAdapter(adapter); editView.setLayoutParams(getTextDefaultParams(field, false)); } return editView; } /** * Add a <CheckBox> single line * * @param label * @param type * @param context * @param mission */ public static void addCheckBox(Field field, LinearLayout mFormView, Context context, MissionFeature feature) { // setting an unique id is important in order to save the state // (content) of this view across screen configuration changes CheckBox cb = new CheckBox(context); cb.setText(field.label); cb.setLayoutParams(getTextDefaultParams(field, false)); cb.setId(id()); cb.setTag(field.fieldId); String selected = (String) feature.properties.get(field.fieldId); if(selected != null){ // Default is false int selection = 0; try { selection = Integer.parseInt(selected); }catch(NumberFormatException nfe){ Log.e(TAG, "Wrong format for: "+field.fieldId+" field"); Log.e(TAG, nfe.getLocalizedMessage(), nfe); selection = 0; } cb.setChecked(selection > 0); } mFormView.addView(cb); } /** * Generates the Android input Type from the text field * * @param field * @return */ private static int getInputType(Field field) { if (field.type == null) { return InputType.TYPE_CLASS_TEXT; } else { switch (field.type) { case person: //return InputType.TYPE_TEXT_VARIATION_PERSON_NAME; // TYPE_TEXT_VARIATION_PERSON_NAME seems to not be used by the framework return InputType.TYPE_TEXT_FLAG_CAP_WORDS; case phone: return InputType.TYPE_CLASS_PHONE; case decimal: case integer: return InputType.TYPE_CLASS_NUMBER; case date: return InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_DATE; case datetime: return InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_NORMAL; case string: return InputType.TYPE_CLASS_TEXT; default: break; } } return InputType.TYPE_CLASS_TEXT; } /** * Provides default Layout Parameters * * @param isLabel * @return */ private static LayoutParams getTextDefaultParams(Field field, boolean isLabel) { LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); if (isLabel) { params.bottomMargin = 5; params.topMargin = 10; } params.leftMargin = 10; params.rightMargin =10; return params; } private static LayoutParams getMapLayoutParams (Field field){ Double h = (Double) field.getAttribute("height"); LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.MATCH_PARENT); if(h!=null){ params = new LayoutParams(LayoutParams.MATCH_PARENT, h.intValue()); }else{ params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); } params.weight = 0.4f; params.bottomMargin = 20; params.topMargin = 20; params.leftMargin = 10; params.rightMargin =10; return params; } /** * get the attribute of a field if present, the default value instead * @param f the field * @param attributeName the name of the attribute * @param defaultValue the default value * @return */ public static Object getAttributeWithDefault(Field f, String attributeName,Object defaultValue){ Object o = f.getAttribute(attributeName); if(o==null){ return defaultValue; } else{ return o; } } }