2013年4月22日 星期一

[iOS] iOS Google Map SDK 與 路徑導航API(Google Maps API v3)

 iOS Google Map SDK

官方網址:
https://developers.google.com/maps/documentation/ios/


首先到https://developers.google.com/maps/documentation/ios/start頁面下載最新版iOS Google Map SDK並照官方教學建立google map,
這部分由於官方教學已夠清楚不再詳述,
記得要註冊取得授權碼。

接者我們使用原生code去接取路徑並繪製到MAP上

前置工作請到https://developers.google.com/maps/documentation/javascript/tutorial打開Google Maps API v3權限,並取得JSON解析器(這邊使用的JSON解析器可以從http://code.google.com/p/json-framework/取得)
之後稍微看一下各參數定義https://developers.google.com/maps/documentation/javascript/reference

這邊提供簡單的範例:
NSString *encodedStringFrom =  (NSString*)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef) @"25,120", NULL, NULL, kCFStringEncodingUTF8);
    NSString *encodedStringTo =  (NSString*)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef) @"26,121"  NULL, NULL, kCFStringEncodingUTF8);
    NSString * url = [NSString stringWithFormat:@"http://maps.googleapis.com/maps/api/directions/json?origin=%@&destination=%@&sensor=false&mode=walking",from,to];
    [encodedStringFrom release];
    [encodedStringTo release];


使用你習慣的HTTP去傳送url並取回NSString *response後進行解析:

     id JSONValue = [response  JSONValue];
        if (JSONValue) {
            NSDictionary *dictionary = (NSDictionary *)JSONValue;
            if ([[dictionary objectForKey:@"status"]isEqual:@"OK"]) {
                NSArray *routes = [dictionary objectForKey:@"routes"];
                for (int i = 0; i < [routes count]; i++) {
                    NSDictionary *rout = [routes objectAtIndex:i];
                    NSDictionary *overview_polyline = [rout objectForKey:@"overview_polyline"];
                    NSString *points = [overview_polyline objectForKey:@"points"];
                    if (polyline) {
                        [polyline remove];//從地圖上移除舊的路線
                    }
                    GMSPolylineOptions *polylineOptions = [GMSPolylineOptions options];
                    polylineOptions.path = [self polylineWithEncodedString:points];
                    polylineOptions.color = [UIColor greenColor];
                    polylineOptions.width = 10.f;
                    polylineOptions.geodesic = YES;
                    if (polylineOptions.path != NULL) {
                        self.polyline = [mapViewRoot addPolylineWithOptions:polylineOptions];
                        didGetPath = TRUE;
                    }
                }
            }
        }


polylineWithEncodedString:

- (GMSMutablePath *)polylineWithEncodedString:(NSString *)encodedString {
    const char *bytes = [encodedString UTF8String];
    NSUInteger length = [encodedString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
    NSUInteger idx = 0;
    
    GMSMutablePath *path = [GMSMutablePath path];
    float latitude = 0;
    float longitude = 0;
    while (idx < length) {
        char byte = 0;
        int res = 0;
        char shift = 0;
        
        do {
            byte = bytes[idx++] - 63;
            res |= (byte & 0x1F) << shift;
            shift += 5;
        } while (byte >= 0x20);
        
        float deltaLat = ((res & 1) ? ~(res >> 1) : (res >> 1));
        latitude += deltaLat;
        
        shift = 0;
        res = 0;
        
        do {
            byte = bytes[idx++] - 0x3F;
            res |= (byte & 0x1F) << shift;
            shift += 5;
        } while (byte >= 0x20);
        
        float deltaLon = ((res & 1) ? ~(res >> 1) : (res >> 1));
        longitude += deltaLon;
        
        float finalLat = latitude * 1E-5;
        float finalLon = longitude * 1E-5;
        [path addCoordinate:CLLocationCoordinate2DMake(finalLat, finalLon)];
    }
    return path;
}


記得要再.h宣告
id<GMSPolyline> polyline //路線
GMSMapView *mapViewRoot //google map


[Titanium] iOS module




The official website Description teaching in more detail:
https://wiki.appcelerator.org/display/guides/iOS+Module+Development+Guide

The following only provides development experience of some specil notes:
  • After build module succeeded,  go to the path of module and unzip the zip file. Then copy modules->iphone->your mdoule   to  user->library->Application Support->Titanium->modules->iphone(here is base on MAC,library is hidden by default)
  • After modified module, you need to delete "build" floder in project before you build project
  • If you did reference C++ file, you will need to modify all the .h file who is reference C++ file to .hh
  • If reference two modules both reference the same lib may cause complie error
  • If  module's define name is different with floder name, it will not be finded when build project.
  • If Titanium dont has the lowest SDK which defined in module, will cause compile error

[Titanium] iOS Module



以下僅提供開發經驗注意事項:
  • 模組建置succeeded後,開啟finder切到該模組目錄下點選zip檔解壓縮出modules,再將modules->iphone->該模組複製到user->資源庫->Application Support->Titanium->modules->iphone下。(此處以MAC環境介紹,資源庫預設為隱藏,需打開顯示)
  • 修改模組後需將引用該模組的project的build資料夾刪除再編譯project
  • 若引用C++檔案需將module內所有相關.h檔改成.hh
  • 若不同module重複引用相同lib可能導致project編譯錯誤
  • 若module內定義名稱與資料夾不同會導致無法找到module
  • 若未安裝module內定義最低Titanium SDK將無法build,需安裝該版SDK或更改最低SDK定義

[Android] unlimit auto marquee TextView

code:
package com.stan.libs.textview;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.TextView;

public class AlwaysMarqueeTextView extends TextView {

 public AlwaysMarqueeTextView(Context context) {
 super(context);
 }

 public AlwaysMarqueeTextView(Context context, AttributeSet attrs) {
 super(context, attrs);
 }

 public AlwaysMarqueeTextView(Context context, AttributeSet attrs, int defStyle) {
 super(context, attrs, defStyle);
 }

 @Override
 public boolean isFocused() {
 return true;
 }
}
Call by xml and set attributes:
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"

[Android] single line TextView auto reszie text size to match area height

Sometimes we want use a single line text witch can just match the height of TextView.
In http://developer.android.com/guide/practices/screens_support.html#screen-independence told us use dimen to adjust text size in different screen size, but we cant really achieve our goal by this way.

Search by Google can find http://stackoverflow.com/questions/5033012/auto-scale-textview-text-to-fit-within-bounds, but this TextView is set to avoid text size out of display bound,
,so I modified the code to make it:

PS. Because it's base on  "for", for better performance.
 I suggest that you also use dimen to set the default value.





package com.stan.libs.textview;

import android.content.Context;
import android.text.Layout.Alignment;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;

public class AutoResizeToFillHeightTextView extends TextView {

 // Minimum text size for this text view
 public static final float MAX_TEXT_SIZE = 999;
 public static final float MIN_TEXT_SIZE = 8;

 // Interface for resize notifications
 public interface OnTextResizeListener {
  public void onTextResize(TextView textView, float oldSize, float newSize);
 }

 // Registered resize listener
 private OnTextResizeListener mTextResizeListener;

 // Flag for text and/or size changes to force a resize
 private boolean mNeedsResize = false;

 // Text size that is set from code. This acts as a starting point for
 // resizing
 private float mTextSize;

 // Temporary upper bounds on the starting text size
 private float mMaxTextSize = MAX_TEXT_SIZE;

 // Lower bounds for text size
 private float mMinTextSize = MIN_TEXT_SIZE;

 // Text view line spacing multiplier
 private float mSpacingMult = 1.0f;

 // Text view additional line spacing
 private float mSpacingAdd = 0.0f;

 // Default constructor override
 public AutoResizeToFillHeightTextView(Context context) {
  this(context, null);
 }

 // Default constructor when inflating from XML file
 public AutoResizeToFillHeightTextView(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
 }

 // Default constructor override
 public AutoResizeToFillHeightTextView(Context context, AttributeSet attrs,
   int defStyle) {
  super(context, attrs, defStyle);
  mTextSize = getTextSize();
 }

 /**
  * If the text view size changed, set the force resize flag to true
  */
 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  if (w != oldw || h != oldh) {
   mNeedsResize = true;
  }
 }

 /**
  * Register listener to receive resize notifications
  * 
  * @param listener
  */
 public void setOnResizeListener(OnTextResizeListener listener) {
  mTextResizeListener = listener;
 }

 /**
  * Override the set text size to update our internal reference values
  */
 @Override
 public void setTextSize(float size) {
  super.setTextSize(size);
  mTextSize = getTextSize();
 }

 /**
  * Override the set text size to update our internal reference values
  */
 @Override
 public void setTextSize(int unit, float size) {
  super.setTextSize(unit, size);
  mTextSize = getTextSize();
 }

 /**
  * Override the set line spacing to update our internal reference values
  */
 @Override
 public void setLineSpacing(float add, float mult) {
  super.setLineSpacing(add, mult);
  mSpacingMult = mult;
  mSpacingAdd = add;
 }

 /**
  * Set the upper text size limit and invalidate the view
  * 
  * @param maxTextSize
  */
 public void setMaxTextSize(float maxTextSize) {
  mMaxTextSize = maxTextSize;
  requestLayout();
  invalidate();
 }

 /**
  * Return upper text size limit
  * 
  * @return
  */
 public float getMaxTextSize() {
  return mMaxTextSize;
 }

 /**
  * Set the lower text size limit and invalidate the view
  * 
  * @param minTextSize
  */
 public void setMinTextSize(float minTextSize) {
  mMinTextSize = minTextSize;
  requestLayout();
  invalidate();
 }

 /**
  * Return lower text size limit
  * 
  * @return
  */
 public float getMinTextSize() {
  return mMinTextSize;
 }

 /**
  * Reset the text to the original size
  */
 public void resetTextSize() {
  if (mTextSize > 0) {
   super.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
  }
 }

 /**
  * Resize text after measuring
  */
 @Override
 protected void onLayout(boolean changed, int left, int top, int right,
   int bottom) {
  if (changed || mNeedsResize) {
   int widthLimit = (right - left) - getCompoundPaddingLeft()
     - getCompoundPaddingRight();
   int heightLimit = (bottom - top) - getCompoundPaddingBottom()
     - getCompoundPaddingTop();
   resizeText(widthLimit, heightLimit);
  }
  super.onLayout(changed, left, top, right, bottom);
 }

 /**
  * Resize the text size with default width and height
  */
 public void resizeText() {
  int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop();
  int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight();
  resizeText(widthLimit, heightLimit);
 }

 /**
  * Resize the text size with specified width and height
  * 
  * @param width
  * @param height
  */
 public void resizeText(int width, int height) {
  CharSequence text = getText();
  // Do not resize if the view does not have dimensions or there is no
  // text
  if (text == null || text.length() == 0 || height <= 0 || width <= 0
    || mTextSize == 0) {
   return;
  }

  // Get the text view's paint object
  TextPaint textPaint = getPaint();

  // Store the current text size
  float oldTextSize = textPaint.getTextSize();
  // If there is a max text size set, use the lesser of that and the
  // default text size
  float targetTextSize = mMaxTextSize > 0 ? Math.min(mTextSize,
    mMaxTextSize) : mTextSize;

  // Get the required text height
  int textHeight = getTextHeight(text, textPaint, width, targetTextSize);

  // Until we either fit within our text view or we had reached our min
  // text size, incrementally try smaller sizes
  while (textHeight > height && targetTextSize > mMinTextSize) {
   targetTextSize = Math.max(targetTextSize - 2, mMinTextSize);
   textHeight = getTextHeight(text, textPaint, width, targetTextSize);
  }

  // Until we either fit within our text view or we had reached our max
  // text size, incrementally try bigger sizes
  while (textHeight < height && targetTextSize < mMaxTextSize) {
   targetTextSize = Math.min(targetTextSize + 2, mMaxTextSize);
   textHeight = getTextHeight(text, textPaint, width, targetTextSize);
  }

  // Some devices try to auto adjust line spacing, so force default line
  // spacing
  // and invalidate the layout as a side effect
  textPaint.setTextSize(targetTextSize);
  setLineSpacing(mSpacingAdd, mSpacingMult);

  // Notify the listener if registered
  if (mTextResizeListener != null) {
   mTextResizeListener.onTextResize(this, oldTextSize, targetTextSize);
  }

  // Reset force resize flag
  mNeedsResize = false;
 }

 // Set the text size of the text paint object and use a static layout to
 // render text off screen before measuring
 private int getTextHeight(CharSequence source, TextPaint paint, int width,
   float textSize) {
  // Update the text paint object
  paint.setTextSize(textSize);
  // Measure using a static layout
  StaticLayout layout = new StaticLayout(source, paint, width,
    Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
  return layout.getHeight();
 }
}

2013年4月16日 星期二

[Android] 單行超出時無限自動跑馬TextView

code:
package com.stan.libs.textview;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.TextView;

public class AlwaysMarqueeTextView extends TextView {

 public AlwaysMarqueeTextView(Context context) {
 super(context);
 }

 public AlwaysMarqueeTextView(Context context, AttributeSet attrs) {
 super(context, attrs);
 }

 public AlwaysMarqueeTextView(Context context, AttributeSet attrs, int defStyle) {
 super(context, attrs, defStyle);
 }

 @Override
 public boolean isFocused() {
 return true;
 }
}
xml調用時須加上屬性:
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"

[Android] single line TextView自動調整text size填滿區域高度

某些情況下我們會想要使用字體Size剛好符合給定區域高度的單行TextView,
參照http://developer.android.com/guide/practices/screens_support.html#screen-independence使用dimen方式來調整卻存在許多問題。

Google後搜尋到http://stackoverflow.com/questions/5033012/auto-scale-textview-text-to-fit-within-bounds的方式,不過該方法是從初值自動縮小以適應高度,與填滿高不太一樣。
因此針對code修改後如下:

注意因為是跑for迴圈去try,size給的初值越接近則效能越好,所以dimen還是設一下巴!





package com.stan.libs.textview;

import android.content.Context;
import android.text.Layout.Alignment;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;

public class AutoResizeToFillHeightTextView extends TextView {

 // Minimum text size for this text view
 public static final float MAX_TEXT_SIZE = 999;
 public static final float MIN_TEXT_SIZE = 8;

 // Interface for resize notifications
 public interface OnTextResizeListener {
  public void onTextResize(TextView textView, float oldSize, float newSize);
 }

 // Registered resize listener
 private OnTextResizeListener mTextResizeListener;

 // Flag for text and/or size changes to force a resize
 private boolean mNeedsResize = false;

 // Text size that is set from code. This acts as a starting point for
 // resizing
 private float mTextSize;

 // Temporary upper bounds on the starting text size
 private float mMaxTextSize = MAX_TEXT_SIZE;

 // Lower bounds for text size
 private float mMinTextSize = MIN_TEXT_SIZE;

 // Text view line spacing multiplier
 private float mSpacingMult = 1.0f;

 // Text view additional line spacing
 private float mSpacingAdd = 0.0f;

 // Default constructor override
 public AutoResizeToFillHeightTextView(Context context) {
  this(context, null);
 }

 // Default constructor when inflating from XML file
 public AutoResizeToFillHeightTextView(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
 }

 // Default constructor override
 public AutoResizeToFillHeightTextView(Context context, AttributeSet attrs,
   int defStyle) {
  super(context, attrs, defStyle);
  mTextSize = getTextSize();
 }

 /**
  * If the text view size changed, set the force resize flag to true
  */
 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  if (w != oldw || h != oldh) {
   mNeedsResize = true;
  }
 }

 /**
  * Register listener to receive resize notifications
  * 
  * @param listener
  */
 public void setOnResizeListener(OnTextResizeListener listener) {
  mTextResizeListener = listener;
 }

 /**
  * Override the set text size to update our internal reference values
  */
 @Override
 public void setTextSize(float size) {
  super.setTextSize(size);
  mTextSize = getTextSize();
 }

 /**
  * Override the set text size to update our internal reference values
  */
 @Override
 public void setTextSize(int unit, float size) {
  super.setTextSize(unit, size);
  mTextSize = getTextSize();
 }

 /**
  * Override the set line spacing to update our internal reference values
  */
 @Override
 public void setLineSpacing(float add, float mult) {
  super.setLineSpacing(add, mult);
  mSpacingMult = mult;
  mSpacingAdd = add;
 }

 /**
  * Set the upper text size limit and invalidate the view
  * 
  * @param maxTextSize
  */
 public void setMaxTextSize(float maxTextSize) {
  mMaxTextSize = maxTextSize;
  requestLayout();
  invalidate();
 }

 /**
  * Return upper text size limit
  * 
  * @return
  */
 public float getMaxTextSize() {
  return mMaxTextSize;
 }

 /**
  * Set the lower text size limit and invalidate the view
  * 
  * @param minTextSize
  */
 public void setMinTextSize(float minTextSize) {
  mMinTextSize = minTextSize;
  requestLayout();
  invalidate();
 }

 /**
  * Return lower text size limit
  * 
  * @return
  */
 public float getMinTextSize() {
  return mMinTextSize;
 }

 /**
  * Reset the text to the original size
  */
 public void resetTextSize() {
  if (mTextSize > 0) {
   super.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
  }
 }

 /**
  * Resize text after measuring
  */
 @Override
 protected void onLayout(boolean changed, int left, int top, int right,
   int bottom) {
  if (changed || mNeedsResize) {
   int widthLimit = (right - left) - getCompoundPaddingLeft()
     - getCompoundPaddingRight();
   int heightLimit = (bottom - top) - getCompoundPaddingBottom()
     - getCompoundPaddingTop();
   resizeText(widthLimit, heightLimit);
  }
  super.onLayout(changed, left, top, right, bottom);
 }

 /**
  * Resize the text size with default width and height
  */
 public void resizeText() {
  int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop();
  int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight();
  resizeText(widthLimit, heightLimit);
 }

 /**
  * Resize the text size with specified width and height
  * 
  * @param width
  * @param height
  */
 public void resizeText(int width, int height) {
  CharSequence text = getText();
  // Do not resize if the view does not have dimensions or there is no
  // text
  if (text == null || text.length() == 0 || height <= 0 || width <= 0
    || mTextSize == 0) {
   return;
  }

  // Get the text view's paint object
  TextPaint textPaint = getPaint();

  // Store the current text size
  float oldTextSize = textPaint.getTextSize();
  // If there is a max text size set, use the lesser of that and the
  // default text size
  float targetTextSize = mMaxTextSize > 0 ? Math.min(mTextSize,
    mMaxTextSize) : mTextSize;

  // Get the required text height
  int textHeight = getTextHeight(text, textPaint, width, targetTextSize);

  // Until we either fit within our text view or we had reached our min
  // text size, incrementally try smaller sizes
  while (textHeight > height && targetTextSize > mMinTextSize) {
   targetTextSize = Math.max(targetTextSize - 2, mMinTextSize);
   textHeight = getTextHeight(text, textPaint, width, targetTextSize);
  }

  // Until we either fit within our text view or we had reached our max
  // text size, incrementally try bigger sizes
  while (textHeight < height && targetTextSize < mMaxTextSize) {
   targetTextSize = Math.min(targetTextSize + 2, mMaxTextSize);
   textHeight = getTextHeight(text, textPaint, width, targetTextSize);
  }

  // Some devices try to auto adjust line spacing, so force default line
  // spacing
  // and invalidate the layout as a side effect
  textPaint.setTextSize(targetTextSize);
  setLineSpacing(mSpacingAdd, mSpacingMult);

  // Notify the listener if registered
  if (mTextResizeListener != null) {
   mTextResizeListener.onTextResize(this, oldTextSize, targetTextSize);
  }

  // Reset force resize flag
  mNeedsResize = false;
 }

 // Set the text size of the text paint object and use a static layout to
 // render text off screen before measuring
 private int getTextHeight(CharSequence source, TextPaint paint, int width,
   float textSize) {
  // Update the text paint object
  paint.setTextSize(textSize);
  // Measure using a static layout
  StaticLayout layout = new StaticLayout(source, paint, width,
    Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
  return layout.getHeight();
 }
}