Montag, 21. März 2011

jQuery Datepicker: buttonImage ausrichten

JQuery-UI 1.8.2

In meinem letzten Beitrag wollte ich das Popup des Datepickers verschieben, nun will ich den Button, mit dem der Datepicker aufgerufen wird etwas anders positionieren.

Vorher noch die Methode, mit der man den Datepicker an ein Inputfeld hängt:

/**
 * Erzeugt einen Kalenderbutton in deutscher Sprache, wenn das Feld nicht disabled ist.
 *
 * @param fieldId: Id des Tags als String
 * @param addToTop: px value to add to the input field.top
 */
function addDatepicker(fieldId, addToTop){
    var $field = $( '#' + fieldId );
 if( $field.length > 0 && $field[0].disabled != true && $field[0].readonly != true && $field.hasClass("textoutput") != true){
     $field.datepicker({showOn: 'button',
       buttonImage: 'img/calendar_icon.gif',
       buttonImageOnly: true,
       buttonText: 'Kalender',
       changeMonth: true,
       yearRange: 'c-20:c+50',
       changeYear: true,
       beforeShow: function(fieldId) {
           var x = 100; //add offset 
           field = $(fieldId);
           left = field.position().left + x;
           /* top - 145 works for above the input */
           setTimeout(function(){
            if(addToTop != undefined || addToTop != null){
                var top = field.position().top + addToTop;
                $('#ui-datepicker-div').css({'top': top + 'px', 'left': left + 'px'});
            } else {
                $('#ui-datepicker-div').css({'left': left + 'px'});
            }
           },1);     
       }
     } );
    }
}


Dazu benutze ich CSS, in diesem ganz einfachen Fall will ich den Button einfach etwas runter setzen und hänge mich dazu an den ui-datepicker-trigger.

.ui-datepicker-trigger {
                               margin-bottom: -3px;
}


Links:
Blogbeitrag mit Lösung

Mittwoch, 9. März 2011

jQuery DatePicker: Popup ausrichten

JQuery-UI 1.8.2

Der Datepicker ist ein unkompliziertes Datumsauswahltool, positioniert man allerdings das aufrufende Element zu weit rechts kann es einem passieren das das Popup aus dem Fenster rutscht. Abhilfe schafft dieses Codeschnippsel:

function addDatepicker(fieldId ){
    var $field = $( '#' + fieldId );
 if( $field.length > 0 && $field[0].disabled != true && $field[0].readonly != true && $field.hasClass("textoutput") != true){
     $field.datepicker({showOn: 'button',
       buttonImage: 'img/calendar_icon.gif',
       buttonImageOnly: true,
       buttonText: 'Kalender',
       changeMonth: true,
       changeYear: true,
       beforeShow: function(fieldId) {
           var x = 100; //add offset 
           field = $(fieldId);
           left = field.position().left + x;
           setTimeout(function(){
               $('#ui-datepicker-div').css({'left': left + 'px'});      
           },1);                    
       }
     } );
    }
}

Der Event beforeShow setzt hier das Popup etwas nach links, damit es ganz sichtbar ist, die Methode setTimeout brauchen wir, da es anders irgendwie nicht so gut klappt.

Links:
Datepicker Dokumentation
Stackoverflow Eintrag mit der Lösung des Problems
Datepicker Demos

Sonntag, 6. März 2011

ListView in Android: TextView und EditText in einer Zeile

Android 2.3.1 Wenn mann aus einem xml-File eine längere Liste von Strings zu Controls auf der Oberfläche verarbeinen will, ist ListView ein geeignetes Mittel, soll nur ein Control pro Zeile vorhanden sein ist der Aufwand wie hier beschrieben minimal.

Sollen mehrere Controls pro Zeile vorhanden sein wird es etwas mehr Aufwand, aus verschiedenen Quellen im Netz hab ich mir soetwas zusammengebaut, ich stelle es nun hier vor:

Die Activity:
public class InputPropertiesActivity extends DefaultListActivity {
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.inputproperties);

  final List rowHolders = new ArrayList();
  String[] rowNames = getResources().getStringArray(R.array.properties);
  for (int i = 0; i < rowNames.length; i++) {
   RowHolder talentholder = new RowHolder(rowNames[i], "");
   rowHolders.add(talentholder);
  }
  setListAdapter(new MyListAdapter(this, R.layout.listrow, rowHolders));
 }
}


Bei der DefaultListActivity handelt es sich nur um eine Klasse die ListActivity extended und ein paar Methoden beinhalted, die ich in allen ListActivities zur Verfügung haben will. Im Prinzip lese ich hier nur mein xml aus und stopfe die Werte zusammen mit einem Leerstring in ein RowHolder Array, das ich meinen MyListAdapter übergebe, den ich dann als ListAdapter festlege.

Der RowHolder:
public class RowHolder {
 String rowName;
 String rowValue;

 public RowHolder(String talentName, String talentValue) {
  this.rowName = talentName;
  this.rowValue = talentValue;
 }

 public String getRowName() {
  return rowName;
 }

 public void setRowName(String rowName) {
  this.rowName = rowName;
 }

 public String getRowValue() {
  return rowValue;
 }

 public void setRowValue(String rowValue) {
  this.rowValue = rowValue;
 }
}


Der ListAdapter:
public class MyListAdapter extends BaseAdapter {

 private final List rowholders;
 private final int rowResID;
 private final LayoutInflater layoutInflater;

 public MyListAdapter(final Context context, final int rowResID, final List rowHolder) {
  this.rowResID = rowResID;
  this.rowholders = rowHolder;

  layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 }

 public int getCount() {
  return rowholders.size();
 }

 public Object getItem(int position) {
  return rowholders.get(position);
 }

 public long getItemId(int position) {
  return position;
 }

 public View getView(final int position, View convertView, ViewGroup parent) {

  final ViewHolder viewHolder;
  final RowHolder rowholder;

  if (convertView == null) {
   viewHolder = new ViewHolder();
   rowholder = rowholders.get(position);
   convertView = layoutInflater.inflate(rowResID, null);
   viewHolder.rowName = (TextView) convertView.findViewById(R.id.rowname);
   viewHolder.rowName.setText(rowholder.getRowValue());


   viewHolder.rowValue = (EditText) convertView.findViewById(R.id.rowvalue);
   viewHolder.rowValue.addTextChangedListener(new TextWatcher() {
    public void afterTextChanged(Editable edit) {
     rowholders.get(viewHolder.ref).setRowValue(edit.toString());
    }

    public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
    }

    public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
    }
   });
   convertView.setTag(viewHolder);
  } else {
   viewHolder = (ViewHolder) convertView.getTag();
  }

  viewHolder.ref = position;
  viewHolder.rowName.setText(rowholders.get(position).getRowName());
  viewHolder.rowValue.setText(rowholders.get(position).getRowValue());
  return convertView;
 }

 class ViewHolder {
  TextView rowName;
  EditText rowValue;
  int ref;
 }
}


Der Aufwand, der hier betrieben wird, ist schon etwas mehr als wenn nur ein Element pro Zeile vorhanden wäre. Allerdings läßt sich obiges wiederverwenden, sind also viele Ansichten ähnlich, müssen aber aus verschiedenen xml´s befüllt werden, nutzt man es einfach nochmal.

Dazu das vereinfachte inputproperties.xml aus dem layout Folder (Die Darstellung der Groß- und Kleinschreibung haut leider so gar nicht hin, muß beim kopieren entsprechend geändert werden):


 
  
  
 



Und das listrow.xml aus dem layout Folder:


 
  
  
 



Wie aber bekommt man die Werte nun wieder raus, wenn ich sie zB. persistieren will? Hierzu habe ich mich wieder des ListAdapters bedient:

Methode saveProperties() der Klasse InputPropertiesActivity:
public void saveProperiets() {

  for (int i = 0; i < this.getListAdapter().getCount(); i++) {
   String rowName = ((RowHolder) this.getListAdapter().getItem(i)).getRowName();
   String rowValue = ((RowHolder) this.getListAdapter().getItem(i)).getRowValue();

   Log.d("NAME - VALUE:", rowName + " " + rowValue);
   if (rowValue == null || rowValue.equalsIgnoreCase("")) {
    showDialog(DIALOG_PROPERTY_MISSING_ID);
   } else {
    inputTalents();
   }
  }
 }


Das Ergebnis (inklusive einiger Modifikationen, mit obigen Code wurde nur die Liste erzeugt) sieht dann so aus, wobei die Anzahl der Elemente beliebig ist:


Links:
Problemstellung zu dieser Konstellation