Showing posts with label Android. Show all posts
Showing posts with label Android. Show all posts

Tuesday, August 6, 2013

ActiveAndroid - Model inheritance

I test ActiveAndroid around and found an issue.

When creating a base super DO class that extends from Model class with some common properties/methods, then create some DOs extending from that base DO.

It turns the extended DOs to miss their properties.

Look into more detail, the problem is from the TableInfo.java

 public TableInfo(Class<? extends Model> type) {  
   mType = type;  
   final Table tableAnnotation = type.getAnnotation(Table.class);  
   if (tableAnnotation != null) {  
     mTableName = tableAnnotation.name();  
   }  
   else {  
     mTableName = type.getSimpleName();  
   }  
   List<Field> fields = new ArrayList<Field>(Arrays.asList(type.getDeclaredFields()));  
   fields.add(getIdField(type));  
   for (Field field : fields) {  
     if (field.isAnnotationPresent(Column.class)) {  
       final Column columnAnnotation = field.getAnnotation(Column.class);  
       mColumnNames.put(field, columnAnnotation.name());  
     }  
   }  
 }  
 private Field getIdField(Class<?> type) {  
   if (type.equals(Model.class)) {  
     try {  
       return type.getDeclaredField("mId");  
     }  
     catch (NoSuchFieldException e) {  
       Log.e("Impossible!", e);  
     }  
   }  
   else if (type.getSuperclass() != null) {  
     return getIdField(type.getSuperclass());  
   }  
   return null;  
 }  

The reason is the above red codes just get its properties but without including its super classes' properties.

So I created a new method getDeclaredFields to replace the getIdField as below:

 /**Get declared fields from the given class & it's ancestors  
  * @param type class  
  * @return  
  */  
 private List<Field> getDeclaredFields(Class<?> type) {  
   List<Field> fields = new ArrayList<Field>();  
   if (type.getSuperclass() != null && type.getSuperclass() != Object.class) {  
     fields.addAll(getDeclaredFields(type.getSuperclass()));  
   }  
   fields.addAll(Arrays.asList(type.getDeclaredFields()));  
   return fields;  
 }  

And replace red lines with:
 List<Field> fields = getDeclaredFields(type);  

Wednesday, July 24, 2013

Horizontal List View with scrollbar

To display list of thumbnail images horizontally on the Android, two images per screen, we use Horizontal ListView from DevsmartLib, but the client would like to show up horizontal scrollbar and position indicator also.

Because the lib doesn't support those features, so we must implement them.

To display the position indicator when scrolling, I add function displayPosition() at the bottom of the onLayout() method:

 @Override   
 protected synchronized void onLayout(boolean changed, int left, int top,    
       int right, int bottom) {   
      ....   
      displayPosition();   
 }   
 private int childWidth=0;  
 private void displayPosition() {   
      String counter = "";  
      if (getChildCount()>0 && mAdapter.getCount()>2) {  
           if (childWidth==0) {  
                childWidth = getChildAt(0).getMeasuredWidth();  
           }  
           double positionX = (double)mCurrentX/childWidth;  
           double fractional = positionX % 1;  
           int position = 1 + (int)positionX;  
           if (fractional > 0.3) position++;  
           counter = String.format(" - %s/%s",position,mAdapter.getCount());  
      }  
      ((Activity) this.getContext()).setTitle("List"+counter);  
 }    
}
To display horizontal scrollbar, it's not easy because it's hard to find examples or good reference, have to google a lot!

Finally found out this, so just implement necessary functions:

 public HorizontalListView(Context context, AttributeSet attrs) {   
   TypedArray a = context.obtainStyledAttributes(R.styleable.View);   
   initializeScrollbars(a);   
   a.recycle();   
   setWillNotDraw(false);   
   setScrollbarFadingEnabled(false);   
   setHorizontalScrollBarEnabled(true);   
 }   
 @Override   
 protected int computeHorizontalScrollOffset() {   
      return this.mCurrentX;   
 }   
 @Override  
 protected int computeHorizontalScrollRange() {  
      return mAdapter.getCount()<3?0:(mAdapter.getCount()*childWidth);  
 }  

The shorten attrs.xml as below:

 <?xml version="1.0" encoding="utf-8"?>  
 <resources>  
 <declare-styleable name="View">  
 <attr name="android:scrollX"/>  
 <attr name="android:scrollY"/>  
 <attr name="android:scrollbarAlwaysDrawHorizontalTrack"/>  
 <attr name="android:scrollbarAlwaysDrawVerticalTrack"/>  
 <attr name="android:scrollbarDefaultDelayBeforeFade"/>  
 <attr name="android:scrollbarFadeDuration"/>  
 <attr name="android:scrollbarSize"/>  
 <attr name="android:scrollbarStyle"/>  
 <attr name="android:scrollbarThumbHorizontal"/>  
 <attr name="android:scrollbarThumbVertical"/>  
 <attr name="android:scrollbarTrackHorizontal"/>  
 <attr name="android:scrollbarTrackVertical"/>  
 <attr name="android:scrollbars"/>    
 </declare-styleable>  
 </resources>  

Note: It's nice to view code in blogger with codeformatter :). Thank you.

Saturday, July 20, 2013

Calculate Google Map zoom level on Android

On Android, Google Map API doesn't have function to export the map to image file directly. But the Google provides Static Maps API to render map into image via http request. An issue comes up when we need to show all locations on the map, so we have to supply the zoom level parameter.

If the map is rendering on the GoogleMap, it's easy to get the center and zoom of the boundary with the code:

 final LatLngBounds.Builder builder = new LatLngBounds.Builder();  
 for (LatLng latlng : list) {  
   builder.include(latlng);   
 }  
 mMap.setOnCameraChangeListener(new OnCameraChangeListener() {  
   @Override  
   public void onCameraChange(CameraPosition arg0) {  
     // Move camera.  
     mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 10));  
     // Remove listener to prevent position reset on camera move.  
     mMap.setOnCameraChangeListener(null);  
     LatLng center = mMap.getCameraPosition().target;  
     float zoom = mMap.getCameraPosition().zoom;  
   }  
 });  

But when we need to export by batch or from the server side, we won't load each map data onto the map view to execute above code. So I find a way to calculate them by formula.

The center point is simple to do:

 final LatLngBounds bounds = builder.build();  
 LatLng ne = bounds.northeast;  
 LatLng sw = bounds.southwest;  
 LatLng center = new LatLng((ne.latitude + sw.latitude)/2,  
             (ne.longitude + sw.longitude)/2);  

There are several ways to calculate the zoom level, but I find this code in Javascript by John S is more accurate (I convert them into java code)

 public static int getBoundsZoomLevel(LatLng northeast,LatLng southwest,   
                   int width, int height) {  
   final int GLOBE_WIDTH = 256; // a constant in Google's map projection  
   final int ZOOM_MAX = 21;  
   double latFraction = (latRad(northeast.latitude) - latRad(southwest.latitude)) / Math.PI;  
   double lngDiff = northeast.longitude - southwest.longitude;  
   double lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;  
   double latZoom = zoom(height, GLOBE_WIDTH, latFraction);  
   double lngZoom = zoom(width, GLOBE_WIDTH, lngFraction);  
   double zoom = Math.min(Math.min(latZoom, lngZoom),ZOOM_MAX);  
   return (int)(zoom);  
 }  
 private static double latRad(double lat) {  
   double sin = Math.sin(lat * Math.PI / 180);  
   double radX2 = Math.log((1 + sin) / (1 - sin)) / 2;  
   return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;  
 }  
 private static double zoom(double mapPx, double worldPx, double fraction) {  
   final double LN2 = .693147180559945309417;  
   return (Math.log(mapPx / worldPx / fraction) / LN2);  
 }