2013-07-10

Android跨平台應用(一) - 運用AD登入

人永遠是不會滿足的,而且老闆更不容易滿足,這是真的!QQ"

老闆不滿足,他也不會讓你滿足(薪水),所以當一個稱職的員工,就要盡力滿足他

好了,廢話說完了,再來進入正題.....

當Android原生寫到一定程度時通常都會想還有什麼可以應用

跨平台應用是會被用到也很常會被使用到的,所以就來往這方式研究

這次來研究如果運用在公司內很常見的AD來登入自己開發的Android應用程式

AD是什麼?AD(Active Directory)說明在這裡

也可以在左邊用Active Directory進維基百科查詢,這裡就不再說明

這樣的目的當然是為了SSO(Single-Sign-On單一登入),不用重覆建立登入資料

看起來好像很方便,實際上.........也是很方便,哈!而且如果不這樣做,老闆是不會滿足的xd

先來說一下原理好了:




佈局:

1.Android應用程式放在Android手機內(廢話..)

2.Web Service放在可對外的Server主機內

3.AD Server放在公司內部Server主機內


說明:

我的設計是手機端經由Web Service傳送帳號密碼給AD Server進行驗證

其實也是可以直接傳給AD Server進行驗證,但為什麼要這樣設計呢?

因為兩個原因:1.可以加密確保帳密的安全
                            2.AD Server IP位置改變時不用重新修改Android應用程式,使用者還要更新
                               只需修改Web Service對應的AD Server 的IP位置即可

光原因二就有必要用這種設計方式了,因為AD Server IP改變後如果使用者沒有更新新的apk檔,基本上這個程式就會出錯,所以中間加一個Web Service彈性是比較高的

再來看一下程式碼:

Web Service
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Data.OleDb;
using System.Collections;
using System.Data;
using System.DirectoryServices;

/// <summary>
/// Search 的摘要描述
/// </summary>
[WebService(Namespace = "http://WebServiceForApp/")]//必須和android手機內的Namespace對應
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// 若要允許使用 ASP.NET AJAX 從指令碼呼叫此 Web 服務,請取消註解下一行。
// [System.Web.Script.Services.ScriptService]
public class Search : System.Web.Services.WebService {
    string _path;
    string _filterAttribute;

    public OleDbConnection Conn = new OleDbConnection(System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);

    public Search () {

        //如果使用設計的元件,請取消註解下行程式碼 
        //InitializeComponent(); 
    }

    [WebMethod]
    public bool Verification(string username,string pwd)
    {
        string domain = "192.168.2.211";//AD Server IP位置
        bool V = true;
        _path = "LDAP://" + domain;
        string domainAndUsername = domain + @"\" + username;
        DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);

        try
        {
            //Bind to the native AdsObject to force authentication.            
            object obj = entry.NativeObject;
            DirectorySearcher search = new DirectorySearcher(entry);

            search.Filter = "(SAMAccountName=" + username + ")";
            search.PropertiesToLoad.Add("cn");
            SearchResult result = search.FindOne();

            //帳號是:it01
            //回覆驗證資料,無為null
            //有Data內容會是這個樣子:LDAP://192.168.2.211/CN=it01,OU=資訊部,OU=管理處,DC=goldjoint,DC=com
            if ((result == null))
            {
                V = false;
            }

            //Update the new path to the user in the directory.
            _path = result.Path;
            _filterAttribute = (string)result.Properties["cn"][0];

        }
        catch (Exception ex)
        {
            //throw new Exception("Error authenticating user. " + ex.Message);
            V = false;
        }


        return V;
    }
}


注意:
手機端我是有使用到ksoap2
所以必須先掛載ksoap2的jar檔進來,以便連結Web Service
網路上掛載的方法很多都有寫,所以這裡就不再重覆,請先掛載。



Android手機端
void WebData()
{
//從網路上獲取資料
String NAMESPACE = "http://WebServiceForApp/";//必須和Web Service內的Namespace對應
String URL = "http://你的WebService位置/AppWebService/Search.asmx";
String SOAP_ACTION = "http://WebServiceForApp/Verification";//NAMESPACE+WebService METHOD_NAME
String METHOD_NAME = "Verification";  

//Data
SoapObject Request = new SoapObject(NAMESPACE, METHOD_NAME); 
PropertyInfo sayHelloPI = new PropertyInfo();
sayHelloPI.setName("username");//加入帳號
sayHelloPI.setValue(id);
sayHelloPI.setType(String.class);
Request.addProperty(sayHelloPI);

PropertyInfo sayHelloPI2 = new PropertyInfo();
sayHelloPI2.setName("pwd");//加入密碼
sayHelloPI2.setValue(pw);
sayHelloPI2.setType(String.class);
Request.addProperty(sayHelloPI2);

//Request.addProperty("ID",strSearch);
SoapSerializationEnvelope soapEnvelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
soapEnvelope.dotNet=true;
soapEnvelope.setOutputSoapObject(Request);
HttpTransportSE aht=new HttpTransportSE(URL);
//aht.debug=true;
try {
aht.call(SOAP_ACTION, soapEnvelope);
//SoapObject result = (SoapObject)soapEnvelope.getResponse();
SoapPrimitive result = (SoapPrimitive)soapEnvelope.getResponse();//單純Data用這個

//存入回傳資料(String Check存入True or False)
check = result.toString();//Check就能拿來檢查是否有AD驗證過了


} catch (SoapFault e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (XmlPullParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}	
}

說明:

有幾個重點要注意:
1.Namespace對應,Web Service和Android手機端的Namespace一定要相同
2.我是有另外專為這個Web Service在IIS上新增一個網站做控管(這點好像不是什麼重點xd)
3.加不加密看狀況,我是走一般網路的80 port,也能走其他的port,但要另外開放和設定
   應用程式封裝成apk檔有一定的安全性了,因為Android 2.3.3以後的版本Google有加入亂碼
   ,程式碼會重排,所以要知道的人還是會知道(應該是自己人xd),如果還是要小心一點可以
   加密,反正可以在Web Service解密就好,這樣就能再送AD Server驗證,本人完全沒加密xd

以上程式碼是可以RUN的,而且也在RUN中,所以如果你不能RUN請留言我幫您看一下為什麼不能RUN。
 
我只取出部份的程式碼,其他部份可能要自己拼湊,不要跟我要全部的程式碼,因為那不是我的,所以我不能給你。


Java Script Window Print-另開視窗方式列印

Web表單設計時還蠻常需要用到列印的,一般都會用Reporting Service

這裡教你用Java Script的方式,另開一個視窗列印表單,順便幫自己記錄程式碼^^


先用DIV框定範圍
<div id="print" align="center">
  表單內HTML程式碼...
 .
 .
 .
 </div>


在Button加入列印OnClientClick事件
<asp:Button ID="Button7" runat="server" Text="列印" OnClientClick="myPrint(document.getElementById('print'));return false;" />


加入JavaScript列印事件
<script type="text/javascript">
        function myPrint(obj) {
            //打開一个新窗口newWindow
            var newWindow = window.open("打印窗口", "_blank");
            //要打印的div的内容
            var docStr = obj.innerHTML;
            newWindow.document.write('<div align="center"><asp:Label ID="lb_printTitle" runat="server" Font-Size="X-Large" style="font-family: 微軟正黑體; font-weight: 700" Text="部門領用統計表"></asp:Label></div>');
            //打印内容寫入newWindow文檔
            newWindow.document.write(docStr);
            //關閉文檔
            newWindow.document.close();
            //調用打印機
            newWindow.print();
            //關閉newWindow頁面
            newWindow.close();
        }
     </script>


畫面(瀏覽器:IE10)
表單

另開列印視窗


注意
因為另開視窗是一個全新的視窗,所以第一次用可能會發現為什麼格式什麼全跑了
那是因為CSS沒有一起過去,所以在newWindow.document.write(.......)要把CSS一起
傳過去,不然就是寫在表單中,這樣出現的表單才會和你想要的一樣。

2013-01-30

Android 手機資料庫(六) - 使用Transaction(交易)

在Activity使用新增、修改、刪除時,有時候都會遇到需要同時處理多筆資料的狀況

這時候如果用一般的寫法,如果在中間處理的資料出錯了,那麼資料就會不完整寫入資料庫了

怎麼辦呢?這個時候就需要用到Transaction(交易),Transaction在很多程式中都有用到

而且是一定會用到,所以我們下面就來看一下在Android系統中該怎麼使用Transaction交易

來達到多筆資料同時處理的目的,其實和其他程式一樣很簡單的。

如果您看過前幾篇資料庫的文章的話,應該會知道其中會有一段db = helper.getWritableDatabase();

只要在這個下面加入:db.beginTransaction();//建立交易

之後就可以正常的用db.insert、db.update等一般資料庫寫入

在最後面都寫完資料庫後得加入此程式碼:db.setTransactionSuccessful();//正式寫入資料庫中

然後我們一般會使用try{}catch{}來抓取出錯誤的訊息,只要在catch中加入db.endTransaction();//結束交易

只要在寫入的過程中有一個出錯了,交易就會中斷,以達到多筆資料同進同出,就這麼簡單

以上就是為您介紹在Android系統中怎麼使用Transaction的方法,謝謝。

2013-01-24

Android 實用程式碼片段(一) - 返回鍵退出(onKeyDown)、返回鍵退出確認

現在應用程式上應該都有按下返回鍵會詢問是否離開的訊息,但這是怎麼做到的呢?

下面為您介紹此實用程式碼:

public boolean onKeyDown(int keyCode, KeyEvent event) {//捕捉返回鍵
        if ((keyCode == KeyEvent.KEYCODE_BACK)) {   
        	ConfirmExit();//按返回鍵,則執行退出確認
        	return true;   
        }   
        return super.onKeyDown(keyCode, event);   
    }
    public void ConfirmExit(){//退出確認
    	AlertDialog.Builder ad=new AlertDialog.Builder(MyOpenDataActivity.this);
    	ad.setTitle("離開");
    	ad.setMessage("確定要離開?");
    	ad.setPositiveButton("是", new DialogInterface.OnClickListener() {//退出按鈕
			public void onClick(DialogInterface dialog, int i) {
				// TODO Auto-generated method stub
				MyOpenDataActivity.this.finish();//關閉activity
 
			}
		});
    	ad.setNegativeButton("否",new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int i) {
				//不退出不用執行任何操作
			}
		});
    	ad.show();//示對話框
    }

只要在onCreate之外加入上面這段程式碼,程式就會在該Activity加入按下返回鍵時的詢問。


2012-12-17

利用檢視原始碼了解在臉書上誰最關注你(妳)

網路上流傳一種用『檢視原始碼』就能知道誰最關注你(妳)的方法

我來給它實作看看:


步驟一:登入你()Facebook到個人首頁


步驟二:在空白處按右鍵,選取『檢視原始碼』




步驟三:搜尋『InitialChatFriendsList


步驟四:List後面一串一串的數字第一個就是最關注你()的人


步驟五:把此串數字加到後面”www.facebook.com/”放到網址上去就知道是誰了



實作的結果真的是我朋友沒錯,不過是不是最關注我的我就不知道了

反正好玩就好了,您說對不對!





2012-12-10

Android下拉選單(spinner)應用(五) - 更換字體大小、顏色

一般網路上都會教一些基本下拉選單(spinner),套用simple_spinner_dropdown_item或

simple_spinner_item這些基本模板,但有時想改變Spinner內的文字大小及顏色又不知從哪下手

今天就來教大家怎麼改變Spinner內的文字大小及顏色,當然也包含對齊方式

我用比較簡單的方法直接由Layout下手去修改Spinner內文字的字體大小及顏色

步驟一:

在res-->layout內新增一個myspinner.xml的android xml檔,貼上下列程式碼

myspinner.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ellipsize="marquee"
    android:gravity="center"
    android:paddingLeft="8dip"
    android:singleLine="true"
    android:textColor="#8B008B"
    android:textSize="32sp"
    android:textStyle="bold" >
</TextView>


步驟二:

回到java程式碼,假設我們已經在main.xml中加入一個下拉選單spinner1了

基本的下拉選單都是這樣寫的:
//將可選内容與ArrayAdapter連接起來 
adapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item,Blood_group);
//對應控件
spinner = (Spinner) findViewById(R.id.spinner1);
//設置下拉列表的風格
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
//將adapter 添加到spinner中  
spinner.setAdapter(adapter); 

換成下面這個樣子:
//將可選内容與ArrayAdapter連接起來 
adapter = new ArrayAdapter<String>(this,R.layout.myspinner,Blood_group);
//對應控件
spinner = (Spinner) findViewById(R.id.spinner1);
//設置下拉列表的風格
adapter.setDropDownViewResource(R.layout.myspinner);
//將adapter 添加到spinner中  
spinner.setAdapter(adapter);


為什麼有兩個地方是要換成myspinner的呢?

因為adapter是代表內容(也就是下拉時)

setDropDownViewResource是代表靜止未點選下拉時的樣式(感謝網友指教)

setDropDownViewResource是代表點選下拉時彈出的樣式

執行後畫面



文字被我改為紫色、字體大小變32、置中對齊

這樣簡簡單單兩個步驟就能改變下拉選單的三種樣式設定了

以上介紹,謝謝各位!!

ps:貼上一些參數(spinner、adapter、Blood_group)的宣告,不然新手會對應不起來
private Spinner spinner;  
 private ArrayAdapter<String> adapter;  
 private static final String[] Blood_group={"A型","B型","O型","AB型","其他"};

2012-12-02

Android TextView應用(一) - 跑馬燈

一般的HTML有跑馬燈功能,那Android應用程式能否用跑馬燈功能呢?可以的!

只要用TextView文字列就可以做出跑馬燈功能了,今天來做的比較特別一點

就是我改用EditText輸入文字後,傳給TextView後做自輸入型的跑馬燈功能

先說一下我的開發環境吧,雖然沒什麼太大影響

我用的Android版本是4.0.3,作業系統是Window 7 64位元,eclipse當然也是for 64位元的

接下來就來看看程式碼吧!


main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/tv_ouput"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="15dp"
        android:textSize="30sp"
        android:singleLine="true"
        android:ellipsize="marquee"
        android:marqueeRepeatLimit="marquee_forever"
        android:focusableInTouchMode="true"
        android:focusable="true"
        android:text="@string/hello_world" />

    <EditText
        android:id="@+id/et_input"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tv_ouput"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="17dp"
        android:ems="10"
        android:hint="請輸入跑馬燈訊息..." >
    </EditText>

    <Button
        android:id="@+id/btn_submit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/et_input"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="28dp"
        android:text="@string/submit" />

</RelativeLayout>






TripleSpinnerActivity
package jim.demo.rundemo;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  final TextView tv=(TextView)findViewById(R.id.tv_ouput);
  
  
  final EditText et = (EditText)findViewById(R.id.et_input);
  
  Button btn = (Button)findViewById(R.id.btn_submit);
  btn.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    tv.setText(et.getText().toString());
    tv.setSelected(true);
   }
   
  });
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.activity_main, menu);
  return true;
 }

}


執行後畫面









說明:

1‧TextView在xml上說明如下:
//單行文本顯示,
android:singleLine="true"
//跑馬燈效果
android:ellipsize="marquee"
//重複次數的設定,默認是無窮次
android:marqueeRepeatLimit="marquee_forever"
//取得焦點,這是讓跑馬燈一開始就可以跑
android:focusableInTouchMode="true"
android:focusable="true"

2.Activity中只差在onClick中設定完文字後
需要用.setSelected(true);讓焦點回到TextView上

3.注意:輸入的文字必須大於TextView的寬度,跑馬燈才會動作
所以如果發現跑馬燈不動作,可以試著在XML中把android:layout_width改小

以上是為你做的說明,有問題歡迎提問,謝謝。

2012-11-07

Android下拉選單(spinner)應用(四) - 三層連動式下拉選單

因為很多人看完了上篇連動式下拉選單後開始問我怎麼再加一層做三層連動下拉選單

而三層下拉在很多地方都會用到,如:最常用到的縣市鄉鎮選取,所以就抽空寫這篇好了

本人用的是最白痴的寫法(因為我懶),其實還有其他寫法,不過這個寫法最快(最懶)

而且最直覺,就直接把二層連動下拉選單中的二維陣列再加一個三維陣列的而已

夠簡單也夠直覺吧(夠懶),下面就直接po程式碼,看完您就會覺得就這樣而已喔

其實也就是這麼簡單而已,我直接從連動式下拉選單改來的,看程式碼吧。

main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#FFFFFF"
    >
    <Spinner android:id="@+id/type" 
        android:layout_width="fill_parent" 
          android:layout_height="45dp"
          android:layout_marginLeft="75dp"
          android:layout_marginRight="20dp"
        android:layout_marginTop="7dp" 
         android:drawSelectorOnTop="true"
         android:layout_alignParentRight="true" 
    />
       <Spinner android:id="@+id/type2" 
        android:layout_width="fill_parent" 
          android:layout_height="45dp"
          android:layout_marginLeft="75dp"
          android:layout_marginRight="20dp"
          android:layout_marginTop="3dp" 
         android:drawSelectorOnTop="true"
         android:layout_alignParentRight="true" 
         android:layout_below="@id/type" 
         />


       <Spinner
           android:id="@+id/type3"
           android:layout_width="fill_parent"
           android:layout_height="45dp"
           android:layout_marginLeft="75dp"
           android:layout_marginRight="20dp"
           android:layout_marginTop="3dp"
           android:drawSelectorOnTop="true"
           android:layout_alignParentRight="true"
           android:layout_below="@+id/type2" />
      
</RelativeLayout>



TripleSpinnerActivity
package jim.demo.TripleSpinner;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.AdapterView.OnItemSelectedListener;

public class TripleSpinnerActivity extends Activity {
 private String[] type = new String[] {"茶類", "果汁類"};//載入第一下拉選單
    private String[] tea = new String[]{"紅茶","綠茶","烏龍綠","青茶"};//起始畫面時預先載入第二下拉選單
    private String[] tea2 = new String[]{"紅茶超好喝","紅茶喝完臉會紅"};//起始畫面時預先載入第三下拉選單
    //第一下拉選取後載入第二下拉選單
    private String[][] type2 = new String[][]{{"紅茶","綠茶","烏龍綠","青茶"},{"柳丁汁","西瓜汁","烏梅汁"}};
    //第二下拉選取後載入第三下拉選單
    private String[][][] type3 = new String[][][]{{{"紅茶超好喝","紅茶喝完臉會紅"},{"綠茶超好喝","綠茶喝完臉會綠"},{"烏龍茶超好喝","烏龍茶超烏龍"},{"青茶超好喝","青茶很尚青"}},{{"柳丁汁超好喝","柳丁汁好棒"},{"西瓜汁超好喝","西瓜汁還可以"},{"烏梅汁超好喝","烏梅汁還可以"}}};
    private Spinner sp;//第一個下拉選單
    private Spinner sp2;//第二個下拉選單
    private Spinner sp3;//第三個下拉選單
    private Context context;

    ArrayAdapter<String> adapter ;

    ArrayAdapter<String> adapter2; 
    
    ArrayAdapter<String> adapter3; 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
         context = this;
        
        //程式剛啟始時載入第一個下拉選單
        adapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item, type); 
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 
        sp = (Spinner) findViewById(R.id.type); 
        sp.setAdapter(adapter);
        sp.setOnItemSelectedListener(selectListener);
        
        //因為下拉選單第一個為茶類,所以先載入茶類群組進第二個下拉選單
        adapter2 = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item, tea); 
        adapter2.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 
        sp2 = (Spinner) findViewById(R.id.type2);
        sp2.setAdapter(adapter2);
        sp2.setOnItemSelectedListener(selectListener2);
        
      //因為下拉選單第二個為紅茶,所以先載入紅茶群組進第三個下拉選單
        adapter3 = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item, tea2); 
        adapter3.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 
        sp3 = (Spinner) findViewById(R.id.type3);
        sp3.setAdapter(adapter3);
        
    }
    
    //第一個下拉類別的監看式
    private OnItemSelectedListener selectListener = new OnItemSelectedListener(){
        public void onItemSelected(AdapterView<?> parent, View v, int position,long id){
         //讀取第一個下拉選單是選擇第幾個
            int pos = sp.getSelectedItemPosition();
            //重新產生新的Adapter,用的是二維陣列type2[pos]
            adapter2 = new ArrayAdapter<String>(context,android.R.layout.simple_spinner_item, type2[pos]);
            //載入第二個下拉選單Spinner
            sp2.setAdapter(adapter2);
        }
      
        public void onNothingSelected(AdapterView<?> arg0){

        }

    };
    
  //第二個下拉類別的監看式
    private OnItemSelectedListener selectListener2 = new OnItemSelectedListener(){
        public void onItemSelected(AdapterView<?> parent, View v, int position,long id){
         //讀取第一個下拉選單是選擇第幾個
            int pos = sp.getSelectedItemPosition();
         //讀取第二個下拉選單是選擇第幾個
            int pos2 = sp2.getSelectedItemPosition();
            //重新產生新的Adapter,用的是三維陣列type3[pos][pos2]
            adapter3 = new ArrayAdapter<String>(context,android.R.layout.simple_spinner_item, type3[pos][pos2]);
            //載入第三個下拉選單Spinner
            sp3.setAdapter(adapter3);
        }
      
        public void onNothingSelected(AdapterView<?> arg0){

        }

    };
}



執行後畫面


選取第二個下拉選單

點選後第二個下拉和第三個下拉選單改變

第三個下拉選單內容也改變了

第一個下拉選單改變會連動其他二個下拉選單改變

說明:
          1.和連動式下拉選單其實改變的沒有很多,只是加入一個Spineer和一個三維陣列

          2.主要的內容要照著三維陣列內的設定,第一個下拉是類別、第二個下拉是品項

             、第三個下拉是評價,所以一開始下拉選單內的預設值因為第一個下拉的類別

             會預設為茶類,而第二下拉會是紅茶,所以第三個下拉就給它一維陣列紅茶的評價

          3.新增的第二個下拉選單選取監看式,new ArrayAdapter<String>產出的是一維陣列

             括號逗點前第一區只是下拉的樣式,所以不用管它,第二區帶入三維陣列

             給三維陣列type3第一和第二下拉的選取項就會產出一維陣列選項讓ArrayAdapter載入

             Spineer3中。

後記:

           以上說明應該清楚了,如有問題歡迎來信指教,謝謝。

2012-10-11

PhoneGap到底是什麼?

最近在忙著幫公司做表單電子化,一個月(11月前)要把兩個表單(外出申請單、忘刷卡單)電子化

忙到我都忘了我有blog這件事了<---有點虎爛!!

今天完成一個表單後為了有在focus我的blog的朋友們

所以今天特別上來寫了這一篇文章<---因為也有人在問!

讓不是很了解PhoneGap的人能再更了解PhoneGap到底是什麼玩意兒?

PhoneGap到底是什麼?是Mobile App?IPhone?App?HTML5?

還有人說是手機應用程式,PhoneGap到底是什麼?

用最簡單再簡單,簡單到一句話就說完的說法就是:

PhoneGap是網頁(HTML5、CSS3)和手機底層功能的中介溝通層套件

請把這句話表框起來,然後放到你的腦海深處不要忘記了

為什麼說它是中介層套件,下面開始為你說明你就會了解:

我們都知道手機應用程式開發分為兩種:Native App和Mobile App

Native App是用原生程式碼,如Android用Java、iPhone用ObjectC...等

Mobile App是用HTML5+手機瀏覽器

Native App我就不廢話直接跳過了

我們針對Mobile App來說明為什麼PhoneGap是Mobile App開發的一個中介溝通層套件

所有的手機應用程式都是APP(廢話),APP 就會需要封裝,這樣才好各別下載(好免費分享...誤)

而PhoneGap也不是開發APP的工具,因為以開發Android是用Eclipse

Eclipse才是開發工具,PhoneGap只能算是開發中的一個套件

而Mobile App有一個大缺點,就是雖然網頁有共通性,不過只要用到手機瀏覽器

手機上的功能你就完全不能用,就算手機能發出七彩霓虹燈、一秒內打電話給林志玲或如花

你也不能用,這是Mobile App最大的缺點,那你會問這跟PhoneGap有什麼關係

怎麼會沒關係,PhoneGap就是在改善這一點的中介溝通層套件(終於說到了!)

在HTML5中用寫JavaScrip的型式就能去呼叫手機內建的功能

這樣你說有沒有很帥,雖然它也不是萬能的,不過最少能加減用點功能也好

以上說明應該夠清楚了,希望不會再有人看完這篇還不知道PhoneGap是什麼?能吃嗎?才好!

最後還是不了解的歡迎寫信問題,我再幫您加強說明,謝謝。

PS:如果以上有錯誤的地方,小弟我也歡迎指教,教學相長,我會很感謝您的。

2012-09-04

PhoneGap API應用:Camera相機(二) - 選取手機內照片用img顯示出來

上篇-用相機拍照後,這篇就不得不說說怎麼從手機內取出照片了

鬼月廢話還是不要太多,不然別人會覺得我是在"鬼話連篇",直接PO上程式碼


JavaScript程式碼
<script type="text/javascript" charset="utf-8" src="phonegap-1.0.0.js"></script>
<script type="text/javascript" charset="utf-8">
       
      var pictureSource;   //圖片的來源
    var destinationType;  //返回值的格式設置

    // Wait for PhoneGap to connect with the device
    document.addEventListener("deviceready",onDeviceReady,false);

    // PhoneGap 剛onload時
    function onDeviceReady() {
        pictureSource=navigator.camera.PictureSourceType;
        destinationType=navigator.camera.DestinationType;
    }
//一個按鈕將調用這個函數
//Android的怪癖:pictureSource.PHOTOLIBRARY和pictureSource.SAVEDPHOTOALBUM是相同的
//所以點選getPhoto(pictureSource.PHOTOLIBRARY)
//或是getPhoto(pictureSource.SAVEDPHOTOALBUM)功能是相同的
    function getPhoto(source) {
      // 取得圖像(成功事件 ,失敗事件,{圖片設定});
      // getpicture(cameraSuccess,cameraError,{cameraOptions});
      navigator.camera.getPicture(onPhotoURISuccess, onFail, { quality: 50, 
        destinationType: destinationType.FILE_URI,
        sourceType: source });
    }
//照片是成功檢索時調用
    function onPhotoURISuccess(imageURI) {
      //取消註釋,以查看base64編碼的圖像數據
      // console.log(imageURI);

      //獲取的圖像處理
      var largeImage = document.getElementById('largeImage');

      //取消隱藏的圖像元素
      largeImage.style.display = 'block';

      //顯示取得的照片
      // The inline CSS rules are used to resize the image
      largeImage.src = imageURI;
    }

    //錯誤發生時 
    function onFail(message) {
      alert('Failed because: ' + message);
    }

      </script>



html元件
<button onclick="getPhoto(pictureSource.PHOTOLIBRARY);">從相片的檔案庫</button><br>
<button onclick="getPhoto(pictureSource.SAVEDPHOTOALBUM);">從相片簿</button><br>
<img style="display:none;width:60px;height:60px;" id="largeImage" src="" />


實作出來的畫面:






說明:

雖然有兩個功能按鈕可以作出這個功能,而且帶入的參數不同

但其實做出來的功能是一樣的,原因是Android的怪癖

Camera.PictureSourceType.PHOTOLIBRARY和PictureSourceType.SAVEDPHOTOALBUM

會被視為相同,都會都出現相同的相冊,官網說明文件中有提到

以上是為您做的簡單介紹,謝謝。

2012-08-30

PhoneGap API應用:Camera相機(一) - 用相機拍照後用img顯示出來

最近因為比較忙一些專案,所以比較沒空寫blog文章

因為之前寫Native App(原生應用程式)比較多,Mobile App的部份相對的就比較少

所以今後會先寫一幾篇Mobile App,Android的開發是很廣泛的,不限定一定要用

怎樣的方式開發,像電子報來說,你可以用Native的方式開發,也能用PhoneGap

的Mobile app方式開發,這都是可行的,所以開發是沒有一定的形式,在於使用

者和設計者的喜好和習慣,這兩種開發方式都有其優缺點,優缺點我在這裡有介紹

過了,各位可以參考,好!以上是我的完全和本文無關的碎碎唸,系列文章第一篇的

廢話都是比較多的請見諒,接下來進入主題。

我會針對PhoneGap API的各大項API做各別應用上的大略介紹,實際在開發上各位還

是要針對需求做自我轉變,我不可能都猜到各位實際開發上會怎麼使用它,如果都猜

得到,我就改行當算命的了XD(誤)。

如果建立、安裝及設定Phonegap因為之前我就有介紹過了PhoneGap安裝,所以之後系列

應用上就都直接貼上程式碼,不再重覆介紹了,請見諒!,直接PO上JavaScript程式碼再來說明。


JavaScript程式碼
<script type="text/javascript" charset="utf-8">
       
       //功用和onload相同
       document.addEventListener("deviceready", onDeviceReady, false);
    
    function onDeviceReady() {
        // Empty
    }
 //一個按鈕將調用這個函數
    function capturePhoto() {
       // 取得圖像(成功事件 ,失敗事件,{圖片設定});
      // getpicture(cameraSuccess,cameraError,{cameraOptions});
      navigator.camera.getPicture(onPhotoDataSuccess, onFail, { quality: 50 });
    }
    //照片是成功檢索時調用
    function onPhotoDataSuccess(imageData) {
      //取消註釋,以查看base64編碼的圖像數據
      // console.log(imageData);

      //獲取的圖像處理
      var smallImage = document.getElementById('smallImage');

      //取消隱藏的圖像元素
      smallImage.style.display = 'block';

      //顯示拍攝的照片
      //內聯 CSS規則是用來調整圖像的大小
      smallImage.src = "data:image/jpeg;base64," + imageData;
    }

    //錯誤發生時 
    function onFail(message) {
      alert('Failed because: ' + message);
    }
      </script>


html元件
<button onclick="capturePhoto();">Capture Photo</button>
<img style="display:none;width:60px;height:60px;" id="smallImage" src="" />


實作出來的畫面:

跳出來的選取相機和Native中的使用其他服務幫忙拍照類似


拍照後,圖片就直接出現在img內



說明:

看得懂JavaScript的應該自己看程式碼就知道了,全部的重點就是
navigator.camera.getPicture(onPhotoDataSuccess, onFail, { quality: 50 });

再來就是顯示圖片 smallImage.src = "data:image/jpeg;base64," + imageData;

以上是為您做的簡單介紹,謝謝。


2012-08-21

Android Adapter家族系列(三) - BaseAdapter

今天介紹Adapter家族內一個本人還蠻常用到的子類別BaseAdapter

幾乎元件在使用自定義的方式呈現時最常出現的一個子類別

因為自定義的view是自由設定的,所以不能像ArrayAdapter只是單純

只帶入資料而已,必須對view內的元件也做設定及加入事件等

也就是說主要的Activity只是建構此子類別,帶入相關需要的參數

其他的自定義view元件想要怎麼加、想要怎麼設定、做什麼事

都是在BaseAdapter內做設定就可以了,這樣說應該聽了還是霧煞煞

我們用實際的程式碼來邊做邊說明好了。

這裡我們用自定義的ListView來做說明:

ListView listView01 = (ListView)findViewById(R.id.listView1); 
     CharSequence[] list = MemorandumDate;
     CharSequence[] list2 = MemorandumNote;
     CharSequence[] list3 = MemorandumPW;
     BaseAdapter adapter = new MyAdapter(this, list, mID, list2, list3, mreminder);  
     listView01.setAdapter(adapter);

說明:
在主要的Activity中,找到ListView元件,然後我們建構一個適配器adapter

CharSequence[]是字元陣列,用來儲存文字用的,我同時在其他地方有宣告三個字元陣列

(list、list2、list3),也分別給於其值(此部份請自己依所需撰寫)及兩個整數陣列(mID、mreminder)

帶入建構子中建構該元件後帶入ListView,要帶入什麼參數和參數的順序是依照規劃MyAdapter這個

BaseAdapter子類別時怎麼開的,這裡就會是需要什麼樣的參數和順序,下面貼出MyAdapter的程式碼。


package jim.memorandum;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class MyAdapter extends BaseAdapter{  
 public LayoutInflater myInflater;
 public Context context;
    CharSequence[] mDate = null;
    CharSequence[] mData = null;
    CharSequence[] mPw = null;
    int[] MID = null;
    int[] mreminder = null;
    
    public MyAdapter(Context ctxt, CharSequence[] mdate,int[] mID,CharSequence[] mdata,CharSequence[] mpw,int[] mreminder){
        myInflater = LayoutInflater.from(ctxt);
        this.mDate = mdate;
        this.MID = mID;
        this.mData = mdata;
        this.context = ctxt;
        this.mPw = mpw;
        this.mreminder = mreminder;
    }

 public int getCount() {
  // TODO Auto-generated method stub
  return MID.length;
 }

 public Object getItem(int position) {
  // TODO Auto-generated method stub
  return MID[position];
 }

 public long getItemId(int position) {
  // TODO Auto-generated method stub
  return position;
 }

 public View getView(int position, View convertView, ViewGroup parent) {
  final int index = position;
         
  ViewTag viewTag;
        
        if(convertView == null){
            
            convertView = myInflater.inflate(R.layout.list_data, null);
            
           
            viewTag = new ViewTag(
                    (TextView)convertView.findViewById(R.id.tv_Date),
                    (TextView)convertView.findViewById(R.id.tv_Data),
                    (ImageButton)convertView.findViewById(R.id.ibtn_edit),
                    (ImageButton)convertView.findViewById(R.id.ibtn_del),
                    (ImageView)convertView.findViewById(R.id.img_remind),
                    (ImageView)convertView.findViewById(R.id.img_lock)
                    );
            
            convertView.setTag(viewTag);
        }
        else{
            viewTag = (ViewTag) convertView.getTag();
        }
        
        //如果有設定密碼就只顯示前五個字
        String stitle =""; 
        if(!mPw[position].toString().equalsIgnoreCase("")){
         stitle = mData[position].toString();
         if(stitle.length() &gt; 3){
         stitle = stitle.subSequence(0, 3) + "...";
         }
         else
         {
          stitle = mData[position].toString();
         }
        }
        else
        {
         stitle = mData[position].toString();
        }
        
        viewTag.tvdata.setText(stitle);
        viewTag.tvdate.setText(mDate[position].toString());    
        
        //提醒
        if(mreminder[index] == 1)
        {
           viewTag.img_reminder.setImageResource(R.drawable.checkbox_on);
        }
        else
        {
          viewTag.img_reminder.setImageResource(R.drawable.checkbox_off);
        }
           
       
        //是否有密碼
        if(!mPw[index].toString().equalsIgnoreCase(""))
        {
           viewTag.img_lock.setImageResource(R.drawable.lock_on);
        }
        else
        {
          viewTag.img_lock.setImageResource(R.drawable.lock_off);
        }
        
        
        //修改按鈕
        final ImageButton btn_edit = (ImageButton) convertView.findViewById(R.id.ibtn_edit);
        btn_edit.setOnClickListener(new View.OnClickListener()  {
          public void onClick(View v)  {
         
         //有密碼時必須輸入密碼
         if(!mPw[index].toString().equalsIgnoreCase(""))
         {
          LayoutInflater factory=LayoutInflater.from(context);
           //得到自定義對話框
                final View DialogView=factory.inflate(R.layout.dialog, null);
                
                AlertDialog.Builder builder = new AlertDialog.Builder(context);
                builder.setTitle("請輸入密碼");
                builder.setCancelable(false);
                builder.setView(DialogView); //設置自定義對話框樣式
             builder.setPositiveButton("送出", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                       try
                         {
                            EditText pw = (EditText)DialogView.findViewById(R.id.pw_dialog);//輸入之密碼
                            //驗證密碼是否相同
                            String sPW1 = mPw[index].toString();//原密碼
                            String sPW2 = pw.getText().toString();//驗證密碼
                            if(sPW1.equals(sPW2))
                            {
                             //相同就進入 
                                Intent intent_request = new Intent(context, Edit.class);
                                String val=String.valueOf(MID[index]);
                                intent_request.putExtra("val", val); 
                                context.startActivity(intent_request);    
                              
                            }else
                            {
                             //不相同時顯示密碼錯誤
                                Toast.makeText(context,"密碼錯誤!", Toast.LENGTH_SHORT).show(); 
                            }                   
                          }catch(Exception e)
                          {
                             e.printStackTrace();
                          }
                  }
             });
             
             builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
              public void onClick(DialogInterface dialog, int id) {
              }
             });  
                
             AlertDialog alert = builder.create();
             alert.show();
             
         }else
         {
          Intent intent_request = new Intent(context, Edit.class);
             String val=String.valueOf(MID[index]);
             intent_request.putExtra("val", val); 
                context.startActivity(intent_request);
         }
          }
        });
        
        //刪除按鈕
        final ImageButton btn_del = (ImageButton) convertView.findViewById(R.id.ibtn_del);
        btn_del.setTag(position);
        btn_del.setOnClickListener(new View.OnClickListener()  {
         
          public void onClick(View v)  {
           AlertDialog.Builder builder = new AlertDialog.Builder(context);
           
           builder.setMessage("確定要刪除?");
           builder.setCancelable(false);
           
           builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int dialog_id) {
           SQLiteDatabase db;
           NewListDataSQL helper = new NewListDataSQL(context, "MemorandumSQL");
           db = helper.getReadableDatabase();
           long long1 = db.delete("newMemorandum", "_ID=" + MID[index], null);               
        //添加成功後返回行號,失敗後返回-1
      if (long1 == -1) {
       Toast.makeText(context,
         "刪除失敗!", Toast.LENGTH_SHORT)
         .show();
      } else {
       Intent intent = new Intent(context, MemorandumActivity.class);
                context.startActivity(intent);
                quit();//關閉,讓back鍵不會出現上一頁
      }
    
                }
            });
           builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
          public void onClick(DialogInterface dialog, int id) {
          }
         });   
         
         AlertDialog alert = builder.create();
         alert.show();
          }
        });
        
        return convertView;
 }
   
    public class ViewTag{
     TextView tvdate;
        TextView tvdata;
        ImageButton ibtnEdit;
        ImageButton ibtnDel;
        ImageView img_reminder;
        ImageView img_lock;
        
        public ViewTag(TextView date, TextView data, ImageButton edit, ImageButton del, ImageView img1, ImageView img2){
            this.tvdate = date;
            this.tvdata = data;
            this.ibtnEdit = edit;
            this.ibtnDel = del;
            this.img_reminder = img1;
            this.img_lock = img2;
            
        }
    }
    
    public void quit() {
        int pid = android.os.Process.myPid();
        android.os.Process.killProcess(pid);
        System.exit(0);
    }
    
}


說明:
1.MyAdapter是繼承自BaseAdapter這個子類別

2.LayoutInflater myInflater 指的是一種擴充,就是說如果你想用自定的XML配置檔

可以用此擴充把自定的XML置檔加入到現有元件中

3.Activity在建構此類別時,需要帶入什麼參數是在MyAdapter 內設定的
如果這個也不了解請麻煩去翻一下物件導向書籍

4.重點在getView內,所有的自定介面設定、處理都在getView內產生及設定、處理

5.有使用一個viewTag的元件設定類別,要告訴介面自定義的XML內有哪些元件配置


貼出自定義的XML配置檔,讓各位更了解崁在ListView內的是怎麼配置的

list_data.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:descendantFocusability="blocksDescendants"
    android:background="@drawable/button_style" >

    <RelativeLayout
        android:id="@+id/relativeLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >


        <TextView
            android:id="@+id/tv_Date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:textSize="20dp" />
        
        
        <ImageView
        android:id="@+id/img_lock"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@+id/img_remind"
        android:layout_alignParentTop="true"
        android:contentDescription="@string/password"
        />
        
        
        <ImageView
        android:id="@+id/img_remind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@+id/ibtn_edit"
        android:layout_alignParentTop="true"
        android:contentDescription="@string/remind"
        />
        

        <ImageButton
            android:id="@+id/ibtn_del"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_alignParentRight="true"
            android:layout_alignParentTop="true"
            android:src="@android:drawable/ic_menu_delete"
            android:contentDescription="@string/del" />


        <ImageButton
            android:id="@+id/ibtn_edit"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_alignParentTop="true"
            android:layout_toLeftOf="@+id/ibtn_del"
            android:src="@android:drawable/ic_menu_edit"
            android:contentDescription="@string/edit" />

    </RelativeLayout>

    <TextView
        android:id="@+id/tv_Data"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>


實際圖型:



後記:
這個是ListView中的自定義圖型,當然這是有美化過的,所以看起來才會有點不一樣

不過如果你想要做到像這個樣子,就一定要用BaseAdapter,然後套用自定義XML配置檔list_data.xml

不然是做不出這樣很自由的變化配置的,以上介紹,謝謝。

2012-08-13

Android Adapter家族系列(二) - ArrayAdapter

這次開始介紹Adapter的各子類別,因為怕太過深入會不容易了解,所以介紹各子類時

都只介紹一些比較基本的應用,如果有想更深入了解的人可以參考官網上面的介紹

ArrayAdapter上面有很完整的說明,謝謝。


我拿ArrayAdapter來橋接Spinner(下拉選單)做為介紹

直接帶入陣列當資料

//定義adapter為ArrayAdapter
ArrayAdapter<String> adapter ;

//建立陣列
private String[] type = new String[] {"茶類", "果汁類"};

//物件化
android.widget.ArrayAdapter.ArrayAdapter<string>(Context context, int textViewResourceId, String[] objects)
//上面是原式,textViewResourceId是下拉選單的樣式

adapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item, type); 

//設定下拉選單點選後出現選單的樣式,使用的是內建預設樣式
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 

//下拉選單
private Spinner sp;

sp = (Spinner) findViewById(R.id.type); 

sp.setAdapter(adapter);


重點:
可能你會發現ArrayAdapter在物件化時,有一個樣式textViewResourceId,
然後setDropDownViewResource又再設定一個樣式,為什麼呢?
因為textViewResourceId指的是下拉選單還未點選時的下拉選單樣式
而setDropDownViewResource設定的是點選下拉選單後跳出選單的樣式
這兩者是不一樣的,這樣說各位應該還是一頭霧水,我用圖片說明好了

new物件時樣式用simple_spinner_item

new物件時樣式用simple_spinner_dropdown_item


setDropDownViewResource物件時樣式用simple_spinner_item

setDropDownViewResource物件時樣式用simple_spinner_dropdown_item

有看清楚了嗎?new物件時textViewResourceId的樣式設定指的是下拉選單出現時的樣式
setDropDownViewResource指的是彈出下拉選單時,出現列表的樣式,此兩者是不同的
當然這兩者也都能使用自定義的佈局XML,只要自己新增一個XML,然後就能在textViewResourceId或setDropDownViewResource裡面選取,選單內的樣式也會跟著改變
以上是為你做ArrayAdapter的簡單介紹,謝謝。

2012-08-10

Android Adapter家族系列(一) - Adapter(適配器)

Adapter(適配器)  官方網站上有對它的一些類別概述:
適配器對象作為AdapterView之間的一個橋樑,並為這一觀點的基礎數據。適配器提供的數據項的訪問。適配器也作出負責的數據集給每個項目。

簡單的來說就是資料和元件間溝通的橋樑,Adapter有以下幾種間接子類別:
ArrayAdapterBaseAdapterCursorAdapterHeaderViewListAdapterListAdapterResourceCursorAdapterSimpleAdapter,SimpleCursorAdapterSpinnerAdapterWrapperListAdapter
這些子類別我們在後續章節再做各別的介紹。

我們常會在ListView、Spinner...等看到這一類別,而且也必須使用到它
我們就來看它常用的公用方法:


getCount ()

How many items are in the data set represented by this Adapter.
適配器內的資料個數

程式碼
public  int getCount() {
   return title.length;
}


GetItem (位置)


Get the data item associated with the specified position in the data set.
獲取的數據在指定位置的數據集相關的項目。


程式碼
public Object getItem(int position) {
  return title[position];
}


getItemId (int position)



Get the row id associated with the specified position in the list.

獲取的行ID列表中的指定位置。

Parameters
position  The position of the item within the adapter's data set whose row id we want.
放置在適配器的數據集的行ID,我們希望該項目的位置。

Returns
The id of the item at the specified position.

返回指定位置編號的項目。

程式碼
public long getItemId(int position) {
   return position;
}


getItemViewType (int position)


Get the type of View that will be created by getView(int, View, ViewGroup) for the specified item.
獲取由getView(INT,查看,ViewGroup的)創建的視圖類型的指定項目


參數
position  該項目內適配器的數據集,我們想要的視圖類型的位置


程式碼
public int getItemViewType(int position) {
  return position ;
}


getView int position, View convertView, ViewGroup parent



Get a View that displays the data at the specified position in the data set. You can either create a View manually or inflate it from an XML layout file. When the View is inflated, the parent View (GridView, ListView...) will apply default layout parameters unless you use inflate(int, android.view.ViewGroup, boolean) to specify a root view and to prevent attachment to the root.
得到一個視圖顯示在指定位置的數據集的數據。你可以手動創建一個視圖從一個XML佈局文件
Parameters (參數)
position(位置)The position of the item within the adapter's data set of the item whose view we want.
進入此方法時是數據集的第幾個了,可做為需要不同項目產生不同資料時判別之用

convertView The old view to reuse, if possible. Note: You should check that this view is non-null and of an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view. Heterogeneous lists can specify their number of view types, so that this View is always of the right type (see getViewTypeCount() and getItemViewType(int)).
簡單來說就是一個view的容器,可以引用新的XML佈局資料達到自定義佈局,再載入資料,最後回傳給需要的元件使用。

parent The parent that this view will eventually be attached to
Returns(返回)
  • A View corresponding to the data at the specified position.  
  • 查看相應的數據在指定的位置
程式碼
public View getView(int position, View convertView, ViewGroup parent) {

  //自訂類別,表達個別listItem中的view物件集合。
        ViewTag viewTag;

        if(convertView == null){
            //取得listItem容器 view
            convertView = myInflater.inflate(R.layout.adapter, null);
            
            //建構listItem內容view
            viewTag = new ViewTag(
            (ImageView)convertView.findViewById(R.id.MyAdapter_ImageView_icon),
            (TextView) convertView.findViewById(R.id.MyAdapter_TextView_title),
            (TextView) convertView.findViewById(R.id.MyAdapter_TextView_info)
             );
            
            //設置容器內容
            convertView.setTag(viewTag);
        }
        else{
            viewTag = (ViewTag) convertView.getTag();
        }
        
        //設定內容圖案
        switch(position){
            case 0:
                viewTag.icon.setBackgroundResource(R.drawable.taipei);
                break;
            case 1:
                viewTag.icon.setBackgroundResource(R.drawable.taichung);
                break;
            case 2:
                viewTag.icon.setBackgroundResource(R.drawable.kaohsiung);
                break;
        }
        
        //設定標題文字
        viewTag.title.setText(title[position]);
        //設定內容文字
        viewTag.info.setText(info[position]);
        
        return convertView;
 }

後記:

Adapter(適配器)還蠻常會看到的,因為如果想客製化介面元件的話,一般都需要用到Adapter(適配器)改變元件為自定義形式,最常用的就是BaseAdapter這個子類,一般形態ArrayAdapter也蠻常用的,不過ArrayAdapter都是用在沒什麼改變時調用,如果想自定義一般都會做用到BaseAdapter這個子類,後續我們再逐步介紹這兩個子類別,謝謝。



Android 工具 - 幫Eclipse 換程式碼顏色

開發Android一般都是Eclipse做為開發工具,但每次都看到相同的程式碼顏色還蠻無聊的

某天我就上網找了一下看能不能改變一下程式碼的顏色換換心情也好,很幸運的也讓我

找到了相關網站有在做這種事情,就讓我們來看看怎麼改變Eclipse的程式碼顏色吧!


進入Eclipse Color Themes選擇你想改變的程式碼顏色 - 小弟我用的是"Sunburst"


選取可以直接安裝的EPF檔下載它


下載後會是一個epf檔


進入Eclipse中匯入該檔,[File] →[Import] →[Gerneral] →[Preferences] →[瀏覽剛才下載的檔案] 


選取剛剛的epf檔按下完成(Finish)


回來看看程式碼全變了

後記:

改變後順眼多了,整個心情也跟著變好(有點誇張!),最少覺得不會那麼刺眼,不過要習慣新的

程式碼配色,像註解就變白了,一開始看會有點不習慣,不過久了就覺得還好了,而且每次看

到的程式碼顏色都一樣很無聊,所以有空可以改一下程式碼顏色也是蠻好玩的,分享給大家

謝謝。

2012-08-08

Android Activity、Intent系列(一) - 萬用的Intent(意圖)

一般人都知道Intent(意圖)是用來幫Activity傳遞參數用的,但如果你的觀念只停留在

Intent只能用來傳遞參數用那就大錯特錯了,下面介紹怎麼使用萬能的Intent幫我們

簡單的就能做很多的事情了。


Activity跳轉-無參數時
Intent it = new Intent(Activity1.this, Activity2.class);
startActivity(it); 



Activity跳轉-有參數時
Intent it = new Intent(Activity1.this, Activity2.class);
Bundle bundle=new Bundle();
bundle.putString("ID", "This is Activity1 message!");
it.putExtras(bundle);
startActivity(it);  



參數接取方的資料處理程式碼
Bundle bundle=getIntent().getExtras();
String name=bundle.getString("ID");



向上一個Activity返回結果
Intent intent=getIntent();
Bundle bundle=new Bundle();
bundle.putString("ID", "This is from Activity2!");
intent.putExtras(bundle);
setResult(RESULT_OK, intent);



返回結果的處理函數
@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode==REQUEST_CODE){
            if(resultCode==RESULT_CANCELED)
                  setTitle("cancle");
            else if (resultCode==RESULT_OK) {
                 String temp=null;
                 Bundle bundle=data.getExtras();
                 if(bundle!=null)   temp=bundle.getString("ID");
                 setTitle(temp);
            }
        }
    }



瀏覽網頁
Uri uri = Uri.parse("http://jim690701.blogspot.tw/");  
Intent it = new Intent(Intent.ACTION_VIEW, uri);  
startActivity(it);



顯示地圖
//輸入經緯度座標呼叫地圖
Uri uri = Uri.parse("geo:38.899533,-77.036476");  
 Intent it = new Intent(Intent.ACTION_VIEW, uri);   
 startActivity(it); 



路徑規劃
Uri uri = Uri.parse("http://maps.google.com/maps?f=d&saddr=startLat%20startLng&daddr=endLat%20endLng&hl=en");  
Intent it = new Intent(Intent.ACTION_VIEW, uri);  
startActivity(it); 



撥打電話
//注意:使用撥號必須在AndroidManifest.xml 中,加上
//<uses-permission id="android.permission.CALL_PHONE" /> 

//呼叫撥號程序
Uri uri = Uri.parse("tel:0800000123");  
Intent it = new Intent(Intent.ACTION_DIAL, uri);  
startActivity(it); 

//直接撥號
Uri uri = Uri.parse("tel:0800000123");  
Intent it = new Intent(Intent.ACTION_CALL, uri);  
startActivity(it);  



寄送SMS/MMS簡訊
//呼叫簡訊程序 
   Intent it = new Intent(Intent.ACTION_VIEW, uri);  
   it.putExtra("sms_body", "The SMS text");   
   it.setType("vnd.android-dir/mms-sms");  
   startActivity(it); 

   //傳送簡訊 
   Uri uri = Uri.parse("smsto://0800000123");  
   Intent it = new Intent(Intent.ACTION_SENDTO, uri);  
   it.putExtra("sms_body", "The SMS text");  
   startActivity(it); 

  //傳送MMS彩訊  
   //取得手機內圖片連結
   Uri uri = Uri.parse("content://media/external/images/media/23");  
   Intent it = new Intent(Intent.ACTION_SEND); 
   //簡訊主題  
   it.putExtra("sms_body", "some text");   
   it.putExtra(Intent.EXTRA_STREAM, uri);  
   it.setType("image/png");   
   startActivity(it); 



傳送E-mail
//呼叫mail程序
Uri uri = Uri.parse("mailto:xxx@abc.com");  
Intent it = new Intent(Intent.ACTION_SENDTO, uri);  
startActivity(it); 


//簡易寄送E-mail
Intent it = new Intent(Intent.ACTION_SEND);  
it.putExtra(Intent.EXTRA_EMAIL, "me@abc.com");  
it.putExtra(Intent.EXTRA_TEXT, "The email body text");  
it.setType("text/plain");  
startActivity(Intent.createChooser(it, "Choose Email Client")); 


//一般寄送E-mail
Intent it=new Intent(Intent.ACTION_SEND);    
String[] tos={"me@abc.com"};    
String[] ccs={"you@abc.com"};
//主要寄送對象    
it.putExtra(Intent.EXTRA_EMAIL, tos);    
//cc對象
it.putExtra(Intent.EXTRA_CC, ccs);    
//Email內容
it.putExtra(Intent.EXTRA_TEXT, "The email body text");  
//Email主題  
it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");    
it.setType("message/rfc822");    
startActivity(Intent.createChooser(it, "選擇電子郵件客戶端"));


//寄送附件
Intent it = new Intent(Intent.ACTION_SEND);  
//Email主題  
it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text"); 
//附件 
it.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/mysong.mp3");
//檔案格式  
sendIntent.setType("audio/mp3");  
startActivity(Intent.createChooser(it, "選擇電子郵件客戶端"));





播放多媒體
//播放手機內的MP3
Uri uri = Uri.parse("file:///sdcard/song.mp3");  
Intent it = new Intent(Intent.ACTION_VIEW, uri);  
it.setType("audio/mp3");  
startActivity(it); 

//播放內存的第一個鈴聲
Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1");  
Intent it = new Intent(Intent.ACTION_VIEW, uri);  
startActivity(it);




google play應用
//尋找某個應用
Uri uri = Uri.parse("https://play.google.com/store/search?q=jim.memorandum"); 
Intent it = new Intent(Intent.ACTION_VIEW, uri);  
startActivity(it);  
//https://play.google.com/store/search?q=這段都一樣
//主要是q後面的搜尋條件,可以是package也可以是名稱


//顯示某個應用的相關信息 
Uri uri = Uri.parse("https://play.google.com/store/apps/details?id=jim.memorandum");  
Intent it = new Intent(Intent.ACTION_VIEW, uri); 
startActivity(it);  
//https://play.google.com/store/apps/details?id=這段都一樣
//主要是id後面的package




Uninstall應用程式
//反安裝必須知道package name
Uri uri = Uri.fromParts("package", strPackageName, null); 
Intent it = new Intent(Intent.ACTION_DELETE, uri);   
startActivity(it); 



讀取QR Code
叫用條碼掃描器
Intent intent = new Intent("com.google.zxing.client.android.SCAN");//開啟條碼掃描器
intent.putExtra("SCAN_MODE", "QR_CODE_MODE");//設定QR Code參數
startActivityForResult(intent, 1);//要求回傳1,回傳值可以自由設定


接收掃描後的回傳值
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 // TODO Auto-generated method stub
 super.onActivityResult(requestCode, resultCode, data);
 
     if (requestCode == 1) { //startActivityForResult回傳值,回傳值是由叫用時帶入的設定
         if (resultCode == RESULT_OK) {
             String contents = data.getStringExtra("SCAN_RESULT");//取得QR Code內容
          txt_hello.setText(contents);//顯示儲存值
  }
            }
 }



後記:

看完以上的Intent應用是不是覺得Intent可以用到的地方還真不少吧

我個人是覺得Intent超好用的需要什麼都可以用Intent去呼叫就好,

簡單又能節省程式碼,以上是對萬用Intent的介紹,謝謝。

2012-08-07

Android Image應用(五) - 縮放、指定圖片大小

此篇要介紹的是如何縮放圖片,我們都知道圖片大小有時在呈現上讓我們不是那麼的滿意

或是讀取進來的圖片尺寸可能都有各式各樣的,這樣很容易破壞我們原來的版型,怎麼辦呢?

今天就介紹怎麼縮放圖片,讓圖片變大、變小、變成我們想要的。

  為了讓讀者比較容易了解及讀取方便,所以我會故意把三個按鈕的處理程序都拆開來寫

,而不是寫在一起,目的是為了易於讀取,不然用程序 + switch case寫在一起就好了


main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <ImageView
            android:id="@+id/imageView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:src="@drawable/ic_launcher" />
    </RelativeLayout>


    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >




        <Button
            android:id="@+id/btn_Center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:text="@string/button_center" />



        <Button
            android:id="@+id/btn_Right"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_marginLeft="30dp"
            android:layout_toRightOf="@+id/btn_Center"
            android:text="@string/button_right" />


        <Button
            android:id="@+id/btn_Left"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_marginRight="30dp"
            android:layout_toLeftOf="@+id/btn_Center"
            android:text="@string/button_left" />

    </RelativeLayout>

</LinearLayout>



strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">圖片縮放</string>
    <string name="button_left">放大</string>
    <string name="button_right">縮小</string>
    <string name="button_center">變為480*480</string>

</resources>


DemoZoomActivity.java
package jim.demo.zoom;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

public class DemoZoomActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
         //圖片
        final ImageView imageView = (ImageView) findViewById(R.id.imageView1);
        
        //放大
        Button btnLeft = (Button)findViewById(R.id.btn_Left);
        //指定大小
        Button btnCenter = (Button)findViewById(R.id.btn_Center);
        //縮小
        Button btnRight = (Button)findViewById(R.id.btn_Right);
        
        
        //放大按鈕事件
        View.OnClickListener listener_left = new View.OnClickListener() {

			@Override
			public void onClick(View arg0) {
				
				//imageView轉Bitmap
				  imageView.buildDrawingCache();
		          Bitmap bmp=imageView.getDrawingCache();
		        		
		        //轉換為圖片指定大小
	      	    //獲得圖片的寬高
	      	    int width = bmp.getWidth();
	      	    int height = bmp.getHeight();
	      	   
	      	    //放大為1.2倍
	      	    float scaleWidth = (float) 1.2;
	      	    float scaleHeight = (float) 1.2;
	      	    
	      	    // 取得想要缩放的matrix參數
	      	    Matrix matrix = new Matrix();
	      	    matrix.postScale(scaleWidth, scaleHeight);
	      	    // 得到新的圖片
	      	    Bitmap newbm = Bitmap.createBitmap(bmp, 0, 0, width, height, matrix,true);
	      	    
	      	  //重新載入 imageView
	      	  imageView.setImageBitmap(newbm);
	          
			} 
        	
        };
        btnLeft.setOnClickListener(listener_left); 
        
        //指定大小按鈕事件
        View.OnClickListener listener_center = new View.OnClickListener() {

			@Override
			public void onClick(View arg0) {
				
				//imageView轉Bitmap
				  imageView.buildDrawingCache();
		          Bitmap bmp=imageView.getDrawingCache();
		        		
		        //轉換為圖片指定大小
	      	    //獲得圖片的寬高
	      	    int width = bmp.getWidth();
	      	    int height = bmp.getHeight();
	      	    // 設置想要的大小
	      	    int newWidth = 480;
	      	    int newHeight = 480;
	      	    // 計算缩放比例
	      	    float scaleWidth = ((float) newWidth) / width;
	      	    float scaleHeight = ((float) newHeight) / height;
	      	    // 取得想要缩放的matrix參數
	      	    Matrix matrix = new Matrix();
	      	    matrix.postScale(scaleWidth, scaleHeight);
	      	    // 得到新的圖片
	      	    Bitmap newbm = Bitmap.createBitmap(bmp, 0, 0, width, height, matrix,true);
	      	    
	      	  //重新載入 imageView
	      	  imageView.setImageBitmap(newbm);
	      	  
			} 
        	
        };
        btnCenter.setOnClickListener(listener_center); 
        
        //縮小按鈕事件
        View.OnClickListener listener_right = new View.OnClickListener() {

			@Override
			public void onClick(View arg0) {
				
				//imageView轉Bitmap
				  imageView.buildDrawingCache();
		          Bitmap bmp=imageView.getDrawingCache();
		        		
		        //轉換為圖片指定大小
	      	    //獲得圖片的寬高
	      	    int width = bmp.getWidth();
	      	    int height = bmp.getHeight();
	      	   
	      	    //縮小為0.8倍
	      	    float scaleWidth = (float) 0.8;
	      	    float scaleHeight = (float) 0.8;
	      	    
	      	    // 取得想要缩放的matrix參數
	      	    Matrix matrix = new Matrix();
	      	    matrix.postScale(scaleWidth, scaleHeight);
	      	    // 得到新的圖片
	      	    Bitmap newbm = Bitmap.createBitmap(bmp, 0, 0, width, height, matrix,true);
	      	    
	      	  //重新載入 imageView
	      	  imageView.setImageBitmap(newbm);
			} 
        	
        };
        btnRight.setOnClickListener(listener_right); 
    }
}


執行畫面





後記:

            如果試著一直放大縮小你會發現一種現象:"失真了",因為把小圖放的太大本來

就會失真,所以最常用的是在讀取圖片後改變圖片比例到我們指定的大小,要不失真也

行,請準備一張大圖,這樣就比較不會失真了,不然系統內的圖片就如同啟始畫面一樣

小小一張而已,放大就會失真,這是圖片一定會有的狀況,謝謝收看。