2014-02-08 36 views

回答

0

2周挖掘和編碼和測試失敗,閱讀和重試後,我都設法把東西在一起:

可能的解決方案:每個

  1. 本機應用程序代碼平臺,但比你有多個應用程序沒有一個
  2. 使用畫布,幾乎不錯,只是在Android上有兩個問題:
    • Android瀏覽器往往會跳過相當多的觸摸事件,所以在畫布上繪製簽名時看起來很可憐​​
    • 在運行2.3和下面你可以面對畫布轉換爲base64困難,因爲功能不支持的設備

我對此的解決方案是: - 使用PhoneGap,並在Android和所有其他設備上使用本機簽名捕獲活動。說起來容易,而不是完成。經過很多努力,我已經設法將它們放在一起,這就是我希望與您分享的內容,這只是骨幹,您可以在自己的項目中按照自己的想法實施它。

首先,您需要安裝phonegap,而不是創建新項目。您將需要以下2個插件:InAppBrowser和Device,請參閱文檔如何獲取此信息。

一旦項目被創建,使用下面的代碼位爲您的index.html(注jq.js是jQuery的的本地副本,以便應用程序可以離線運行):

<html> 
<head> 
     <title>Simple Signature Capture</title> 
</head> 
<body> 
    <div> 
    <input type=button value='GET SIGNATURE' id=sign> 
    </div> 
</body> 
</html> 

    <script type="text/javascript" src="phonegap.js"></script> 
    <script type="text/javascript" src="jq.js"></script> 
    <script type="text/javascript"> 
    var signatrue=""; 
     $("#sign").click(function(){ 

     if (device.platform!=='Android') { 
     // Use canvas method to get signature if device is different than Android 
     var ref = window.open('sign.html', '_blank', 'location=no'); 
     ref.addEventListener('loaderror', function(event) { ref.close(); signatrue=event.url; print_picture();}); 
        //As you see force the error listener and we assign the URL value to our variable 
     } 
     // Use native android activity to get signature 
     else { 
        // call to native function which starts the new intent 
     window.HelloWorld.customFunctionCalled(); 
        // function which will be called by the intent on result 
     function got_signature(){ 
        // call to native function which will give us a variable which contains the signature in base4 format 
     signature=window.HelloWorld.send_picture(); 
     print_picture(); 
     } 
     } 
     }); 
function print_picture() 
{ 
alert(signatrue); 
} 
</script> 

現在,我們需要一個名爲sign.html如果我們不能在Android上,這將抓住我們的簽名,這裏是代碼(注意jq.js是jQuery的同一本地副本):

<style> 
    .container, html, body{ 
    height:100%; 
    width:100%; 
    overflow:none; 
    padding:0; 
    margin:0; 
    } 
    input[type=button] { 
    width:50%; 
    height:auto; 
    text-align:center; 
    font-size:18pt; 
    float:left; 
    } 
    .sign_place 
    { 
    width:100%; 
    clear:both; 
    height:auto; 
    } 
    </style> 
    <script type="text/javascript" src="jq.js"></script> 
    <html> 
    <div class=container> 
    <input type=button id=clear value='Clear'><input type=button id=save value='Save'> 
    <div class="sign_place"></div> 
    </div> 
    </html> 
    <script> 
    //reset the canvas 
    $("#clear").click(function(){canvas.width = canvas.width;}); 
    // save the canvas and pass back the value to the parent - very dirty and cheap to force and error page so we can catch the string in the main window. 
    //data:image/png;base64, beginning must be removed otherwise browser will display the freshly captured image 
    $("#save").click(function(){var pngUrl = canvas.toDataURL().replace("data:image/png;base64,",""); window.location.href=pngUrl; }); 
    width=$("html").width()-10; 
    height=$("html").height()-$("#clear").height()-10; 
    $(".sign_place").html("<canvas id='signpad' width="+width+" height="+height+" style='padding:0pxmargin:0px;border:0px solid black;'>Sorry, your browser is not supported.</canvas>"); 
    var canvas = document.getElementById('signpad'); 
    var context = canvas.getContext('2d'); 
     // create a drawer which tracks touch movements 
     var drawer = { 
      isDrawing: false, 
      touchstart: function (coors) { 
      context.beginPath(); 
      context.moveTo(coors.x, coors.y); 
      this.isDrawing = true; 
      }, 
      touchmove: function (coors) { 
       if (this.isDrawing) { 
        context.lineTo(coors.x, coors.y); 
        context.lineJoin = 'round'; 
        context.lineWidth=5; 
        // adjust the lineWidth to look good. 
        context.stroke(); 

       } 
      }, 
      touchend: function (coors) { 
       if (this.isDrawing) { 
        this.touchmove(coors); 
        this.isDrawing = false; 
       } 
      } 
     }; 
     // create a function to pass touch events and coordinates to drawer 
     function draw(event) { 
      var type = null; 

      var coors; 
      if(event.type === "touchend") { 
       coors = { 
        x: event.changedTouches[0].pageX, 
        y: event.changedTouches[0].pageY 
       }; 
      } 
      else { 
       // get the touch coordinates 
       coors = { 
        x: event.touches[0].pageX, 
        y: event.touches[0].pageY 
       }; 
      } 
      type = type || event.type 
      // pass the coordinates to the appropriate handler 
      drawer[type](coors); 
     } 
     // Listen for touch events and draw 
      document.addEventListener('touchstart', draw, false); 
      document.addEventListener('touchmove', draw, false); 
      document.addEventListener('touchend', draw, false);   
     // prevent elastic scrolling 
     document.body.addEventListener('touchmove', function (event) { 
      event.preventDefault(); 
     }, false); // end body.onTouchMove 
     // Redo the canvas on orientation change 
     window.addEventListener('orientationchange', function(){ 
     // new to reduce max width and height otherwise canvas will be larger than the screen :(
     width=$("html").width()-10; 
     height=$("html").height()-$("#clear").height()-10; 
     // do not use CSS to adjust it it will stretch the signature and it will look unrealistic. 
     $("#signpad").attr('width',width); 
     $("#signpad").attr('height',height); 

     }); 

    </script> 

都好,幾乎準備好在你做之前,確保你保存了jq.js名稱下的當前jQuery到你的www文件夾 使用phonegap爲所需的平臺(iOS,Android,Windows Phone,BB)構建應用程序。

後你蓋了,去了Android構建和修改的主要活動的java以下幾點:

public class HelloWorld extends CordovaActivity 
{ 
//We will need this global string to pass the signature from an activity to our phonegap app 
String signature=""; 

@Override 
public void onCreate(Bundle savedInstanceState) 
{ 
    super.onCreate(savedInstanceState); 
    super.init(); 
    // Enable the Java <-> Javascript interaction 
    appView.addJavascriptInterface(this, "HelloWorld"); 
    // Set by <content src="index.html" /> in config.xml 
    super.loadUrl(Config.getStartUrl()); 
    //super.loadUrl("file:///android_asset/www/sign.html"); 
} 

//@JavascriptInterface annotation must be used for compatibility with Android 4.2 and above 
@JavascriptInterface 
public void customFunctionCalled() { 
Intent intent = new Intent(HelloWorld.this, SignatureCapture.class); 
startActivityForResult(intent,1); 

    } 
    @JavascriptInterface 
    public String send_picture() { 
     return signature; 
    //This function only sends back the signature which is already is Base64 to the Javascript. 
    } 
    @JavascriptInterface 
    protected void onActivityResult(int requestCode, int resultCode, Intent data) { 

     if (requestCode == 1) { 

     if(resultCode == RESULT_OK){  
      signature = data.getStringExtra("RESULT_STRING"); 
      // Dirty trick to send Javascript command to PhoneGap 
      // The trick will call the function got_signature() in Javascript 
      // Which than will call the Java function get_picture 
      // PhoneGap will NOT work if we try to send the image here with 
      // something like super.sendJavascript("got_signature(var signature="+signature+");"); 
      // so do not even try to do that, it took me couple of hours 
      // till I realized that there is a bug somewhere in phonegap 
      super.sendJavascript("got_signature();"); 
     } 
     if (resultCode == RESULT_CANCELED) {  
      //Write your code if there's no result 
     } 
     } 
    } 
} 

創建一個名爲SignatureCapture新類,請確保你在你的mainfest文件中定義它,以及:

public class SignatureCapture extends Activity { 
LinearLayout mContent; 
    signature mSignature; 
    Button mClear, mGetSign, mCancel; 

    public int count = 1; 
    public String current = null; 
    private Bitmap mBitmap; 
    View mView; 


    private String uniqueId; 
    String ba1=""; 
    //private EditText yourName; 

    @Override 
    public void onCreate(Bundle savedInstanceState) 
    { 
     super.onCreate(savedInstanceState); 
     this.requestWindowFeature(Window.FEATURE_NO_TITLE); 
     setContentView(R.layout.signature); 


     //prepareDirectory(); 
     uniqueId = getTodaysDate() + "_" + getCurrentTime() + "_" + Math.random(); 
     current = uniqueId + ".png"; 
     //mypath= new File(directory,current); 


     mContent = (LinearLayout) findViewById(R.id.linearLayout); 
     mSignature = new signature(this, null); 
     mSignature.setBackgroundColor(Color.WHITE); 
     mContent.addView(mSignature, LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); 
     mClear = (Button)findViewById(R.id.clear); 
     mGetSign = (Button)findViewById(R.id.getsign); 
     mGetSign.setEnabled(false); 

     mView = mContent; 



     mClear.setOnClickListener(new OnClickListener() 
     {   
      public void onClick(View v) 
      { 
       Log.v("log_tag", "Panel Cleared"); 
       mSignature.clear(); 
       mGetSign.setEnabled(false); 
      } 
     }); 

     mGetSign.setOnClickListener(new OnClickListener() 
     {   
      public void onClick(View v) 
      { 
       Log.v("log_tag", "Panel Saved"); 
       boolean error = captureSignature(); 
       if(!error){ 
        mView.setDrawingCacheEnabled(true); 
        mSignature.save(mView); 
        Bundle b = new Bundle(); 
        b.putString("status", "done"); 
        Intent intent = new Intent(); 
        intent.putExtras(b); 
        intent.putExtra("RESULT_STRING",ba1); 
        setResult(RESULT_OK,intent); 
        finish(); 
       } 
      } 
     }); 



    } 

    @Override 
    protected void onDestroy() { 
     Log.w("GetSignature", "onDestory"); 
     super.onDestroy(); 
    } 

    private boolean captureSignature() { 

     boolean error = false; 
     String errorMessage = ""; 


//  if(yourName.getText().toString().equalsIgnoreCase("")){ 
//   errorMessage = errorMessage + "Please enter your Name\n"; 
//   error = true; 
//  } 

     if(error){ 
      Toast toast = Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT); 
      toast.setGravity(Gravity.TOP, 105, 50); 
      toast.show(); 
     } 

     return error; 
    } 

    private String getTodaysDate() { 

     final Calendar c = Calendar.getInstance(); 
     int todaysDate =  (c.get(Calendar.YEAR) * 10000) + 
     ((c.get(Calendar.MONTH) + 1) * 100) + 
     (c.get(Calendar.DAY_OF_MONTH)); 
     Log.w("DATE:",String.valueOf(todaysDate)); 
     return(String.valueOf(todaysDate)); 

    } 

    private String getCurrentTime() { 

     final Calendar c = Calendar.getInstance(); 
     int currentTime =  (c.get(Calendar.HOUR_OF_DAY) * 10000) + 
     (c.get(Calendar.MINUTE) * 100) + 
     (c.get(Calendar.SECOND)); 
     Log.w("TIME:",String.valueOf(currentTime)); 
     return(String.valueOf(currentTime)); 

    } 



    public class signature extends View 
    { 
     private static final float STROKE_WIDTH = 5f; 
     private static final float HALF_STROKE_WIDTH = STROKE_WIDTH/2; 
     private Paint paint = new Paint(); 
     private Path path = new Path(); 

     private float lastTouchX; 
     private float lastTouchY; 
     private final RectF dirtyRect = new RectF(); 

     public signature(Context context, AttributeSet attrs) 
     { 
      super(context, attrs); 
      paint.setAntiAlias(true); 
      paint.setColor(Color.BLACK); 
      paint.setStyle(Paint.Style.STROKE); 
      paint.setStrokeJoin(Paint.Join.ROUND); 
      paint.setStrokeWidth(STROKE_WIDTH); 
     } 

     public void save(View v) 
     { 
      Log.v("log_tag", "Width: " + v.getWidth()); 
      Log.v("log_tag", "Height: " + v.getHeight()); 
      if(mBitmap == null) 
      { 
       mBitmap = Bitmap.createBitmap (mContent.getWidth(), mContent.getHeight(), Bitmap.Config.RGB_565);; 
      } 
      Canvas canvas = new Canvas(mBitmap); 
      v.draw(canvas); 

      ByteArrayOutputStream bao = new ByteArrayOutputStream(); 
      mBitmap.compress(Bitmap.CompressFormat.PNG, 70, bao); 
      byte [] ba = bao.toByteArray(); 

      ba1=Base64.encodeToString(ba,Base64.DEFAULT); 
      //Toast.makeText(SignatureCapture.this, ba1, Toast.LENGTH_LONG).show(); 
     } 

     public void clear() 
     { 
      path.reset(); 
      invalidate(); 
     } 

     @Override 
     protected void onDraw(Canvas canvas) 
     { 
      canvas.drawPath(path, paint); 
     } 

     @Override 
     public boolean onTouchEvent(MotionEvent event) 
     { 
      float eventX = event.getX(); 
      float eventY = event.getY(); 
      mGetSign.setEnabled(true); 

      switch (event.getAction()) 
      { 
      case MotionEvent.ACTION_DOWN: 
       path.moveTo(eventX, eventY); 
       lastTouchX = eventX; 
       lastTouchY = eventY; 
       return true; 

      case MotionEvent.ACTION_MOVE: 

      case MotionEvent.ACTION_UP: 

       resetDirtyRect(eventX, eventY); 
       int historySize = event.getHistorySize(); 
       for (int i = 0; i < historySize; i++) 
       { 
        float historicalX = event.getHistoricalX(i); 
        float historicalY = event.getHistoricalY(i); 
        expandDirtyRect(historicalX, historicalY); 
        path.lineTo(historicalX, historicalY); 
       } 
       path.lineTo(eventX, eventY); 
       break; 

      default: 
       debug("Ignored touch event: " + event.toString()); 
       return false; 
      } 

      invalidate((int) (dirtyRect.left - HALF_STROKE_WIDTH), 
        (int) (dirtyRect.top - HALF_STROKE_WIDTH), 
        (int) (dirtyRect.right + HALF_STROKE_WIDTH), 
        (int) (dirtyRect.bottom + HALF_STROKE_WIDTH)); 

      lastTouchX = eventX; 
      lastTouchY = eventY; 

      return true; 
     } 

     private void debug(String string){ 
     } 

     private void expandDirtyRect(float historicalX, float historicalY) 
     { 
      if (historicalX < dirtyRect.left) 
      { 
       dirtyRect.left = historicalX; 
      } 
      else if (historicalX > dirtyRect.right) 
      { 
       dirtyRect.right = historicalX; 
      } 

      if (historicalY < dirtyRect.top) 
      { 
       dirtyRect.top = historicalY; 
      } 
      else if (historicalY > dirtyRect.bottom) 
      { 
       dirtyRect.bottom = historicalY; 
      } 
     } 

     private void resetDirtyRect(float eventX, float eventY) 
     { 
      dirtyRect.left = Math.min(lastTouchX, eventX); 
      dirtyRect.right = Math.max(lastTouchX, eventX); 
      dirtyRect.top = Math.min(lastTouchY, eventY); 
      dirtyRect.bottom = Math.max(lastTouchY, eventY); 
     } 
    } 
} 

現在你只需要定義佈局新類:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
android:id="@+id/linearLayout1" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:orientation="vertical" > 

<LinearLayout android:layout_height="wrap_content" 
    android:id="@+id/linearLayout2" android:layout_width="match_parent"> 
    <Button android:layout_height="50dp" android:layout_weight=".35" 
     android:text="Clear" android:layout_width="0dp" android:id="@+id/clear" /> 
    <Button android:layout_height="50dp" android:layout_weight=".35" 
     android:text="Save" android:layout_width="0dp" android:id="@+id/getsign" /> 
</LinearLayout> 
<TableLayout android:layout_height="wrap_content" 
    android:id="@+id/tableLayout1" android:layout_width="match_parent"> 
    <TableRow android:id="@+id/tableRow1" android:layout_width="wrap_content" 
     android:layout_height="wrap_content"> 
    </TableRow> 
    <TableRow android:id="@+id/tableRow3" android:layout_width="wrap_content" 
     android:layout_height="wrap_content"> 
    </TableRow> 
</TableLayout> 
<LinearLayout android:layout_height="match_parent" 
    android:id="@+id/linearLayout" android:layout_width="match_parent" /> 
</LinearLayout> 

現在或多或少就是這樣。請注意,我並不是寫活動代碼的人,並且這些優點都不是我的,我只是簡單地找到了一種如何將它放在一起的方式。

我希望你們中的一些人會覺得它有用。