2016年8月13日 星期六

如何使用EWS Api 使 Outlook Calendar 在 Android 上同步化

前言:
這件事情讓我研究了一段時間,因為官方有兩個方案:一個是用REST Api 來同步Android專案,這個稍微有點複雜,我也嘗試寫了幾個Demo但是效果感覺不好,因為他沒辦法自己設定登陸畫面(Login),官方會直接將App轉入他們自己的畫面(這有點類似WebView的感覺),所以感覺醜醜的.... 也沒什麼安全感
以下是官方的QickStart的GitHub連結: 大家可以參考一下
https://github.com/OfficeDev/Outlook-SDK-Android
畫面截圖:






















以上就是他的登陸畫面,如果這麼多東西放在一個小小的手機裡頭,用戶應該會摔手機...或這刪除程式 .....



所以接下來我要介紹第二個方案, EWS for Android
 在網路上都會看到這個:
https://github.com/OfficeDev/ews-java-api
但 Android Studio無法運行這個第三方Lib,這只是For Java only的Lib,我建議大家可以在裡頭的wiki 看如何調用它的Api方式,等等會介紹我上網找到的Lib並且已經成功放置功能在專案裡頭了!
PS:如果有人有其他更好的意見或者Third party Library歡迎提供留言呦!


正文:
首先先找到這個Git專案:
https://github.com/vanan08/AndroidExchange-EWS-

並運行裡頭的TestEWS,如果一切都在預料之中,在App填入帳號密碼並且填入標題,就可以實作完成了。






















看到這裡有沒有很簡單?接下來只要參考這個專案,在參加所需要的功能後就大功告成了!
詳情請參考:
https://github.com/OfficeDev/ews-java-api/wiki


接下來我會稍微簡單的介紹一下如何使用這個Liberary來騙騙字數,其實以下的內容的都包含在wiki


  • 第一步:匯入專案
由於小弟實在是很懶,所以直接把"eWSForAndroid"的Lib 放入了Android專案裡頭。

首先先將"eWSForAndroid" 複製貼上到專案裡頭,接著你會發現在Android Studio會找不到這個專案,這個時候不要驚慌。


  • 進入Gradle Scripts / setting.gradle  增加  include ':eWSForAndroid' 
include ':app'
include ':eWSForAndroid'


  • 接下來進入 Gradle Scripts / build.gradle 匯入專案 : 

compile project(':eWSForAndroid')


程式碼如下:
apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "ching.outlook"
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile project(':eWSForAndroid')
}





  • 進入MainActivty,最後在把根據官方提供的Api寫進去就大功告成了:
        ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
        ExchangeCredentials credentials = new WebCredentials("book887118@outlook.com", "Aa7874747474");  //輸入自己的帳號密碼
        service.setCredentials(credentials);
        try {
            service.autodiscoverUrl("book887118@outlook.com"); /輸入自己的帳號
        } catch (Exception e) {
            e.printStackTrace();
        }




設定service 就可利用EWS api來實現想要實現的功能呦!

PS:如果有不會,或者是有更正的地方歡迎在底下留言^^


附上專案源碼:https://github.com/book87118/Android_Googlecalendar_Outlookcalender_Sync





2016年7月26日 星期二

Android - RecyclerView 初級創建(初級解析)

前言:
RecyclerView是 Google釋出的新元件,用來取代ListView跟GridView 以及StraggeredGridView,只要設定好LayoutManager就可以輕鬆改變佈局,非常的實用。網路上有非常多的教程,程式碼參考(複制)  Squire Island  裏頭的設計方式給我很大的啟發,以及Android RecylcerView藝術般的控件 堪稱入門神器XDD 
         如下圖所示:要使用RecycleView 需要設定LayoutManager來決佈局,以及Adapter (RecyclerView.Adapter)   並用Adapter來接收Data






  • 第一步:添加第三方控件
首先要在gradle的dependencies裏頭添加控件包:recyclerView

compile 'com.android.support:recyclerview-v7:23.2.0':
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support:recyclerview-v7:23.2.0'

}


如果沒有加入控件包的話繼續下一步會業障重喔XDD



  • 第二步:在XML添加RecyclerView元件

首先進入res/layout 找到 activity_main.xml   加入RecyclerView:
 <android.support.v7.widget.recyclerview 
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/recycler_view"
 android:layout_height="match_parent"
 android:layout_width="match_parent"
 android:layout_centerhorizontal="true"
 android:layout_centervertical="true"/>


  • 接著我們要加入ViewHolder,設定ViewHolder從簡單來說,裏頭的內容決定了每一個格子會有什麼元件:舉下圖來看好了,裏頭的Item 0~3就只有一個TextView而已!
  • 這時候我們繼續在res/layout裡頭,點擊右鍵創建一個新的Layout,我決定把它取名叫做....... Hmmm.....就決定是Rick惹!?(被巴)
    取好名字叫做 recyclerview_holder以後,我們繼續在裡頭創建一個非常簡單的TextView代碼如下:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

              android:orientation="vertical"

              android:layout_width="match_parent"

              android:layout_height="wrap_content"

              android:paddingRight="10dp">

    <TextView

        android:id="@+id/text"

        android:layout_height="wrap_content"

        android:layout_width="match_parent"

        android:layout_gravity="center"

        android:textSize="@dimen/text_size"

        android:background="@color/colorLightBule" />



</LinearLayout>





  • 第三步:初始化RecyclerVIew:
我們先假設已經寫好了RecyclerView的Adapter以及等等會用得ItemDecoration,現在一起把所有的東西都設定好吧!

public class MainActivity extends AppCompatActivity {

    private RecyclerView mRecyclerView;
    private RecyclerView.LayoutManager mLayoutManager;
    private List mDatas;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        putDataBase();
        
        settingRecycleView();

    }
    private void putDataBase(){
        mDatas = new ArrayList();
        for(int i =0 ; i < 50 ; i++) {
            mDatas.add(String.valueOf(i));
        }

    }

    private void settingRecycleView(){
        mRecyclerView = (RecyclerView)findViewById(R.id.recycler_view);

        mRecyclerView.setHasFixedSize(true);

        mRecyclerView.addItemDecoration(new MarginDecoration(this, MarginDecoration.VERTICAL_LIST));

        mLayoutManager = new StaggeredGridLayoutManager((2), StaggeredGridLayoutManager.VERTICAL);

        mRecyclerView.setLayoutManager(mLayoutManager);

        mRecyclerView.setAdapter(new RecycleViewAdapter(mDatas));
    }

}



在settingRecyclerView的部分,我們增加了等等會提到的ItemDecoration 以及設定好 mLayoutManager 是 StaggeredGridLayoutManager。
在這裡必需值得一提的是還提供LinearLayoutManager跟GridLayoutManager,當然也可以自訂自己需要的Manager。

在這裡我選擇了StaggeredGridLayoutManager :瀑布流(中文名字感覺很像某種拳法?!)

StaggeredGridLayout主要的特徵是可以隨著內容的多寡來調整,如下圖所示,測試測試測試測試測試~~~~  的欄位佔了左上角,右邊的排序就變成 右邊0 - 1  - 2 -  左邊 3
如果把 StaggeredGridLayoutManager 改成 GridLayoutManager,右邊的 1,會跟左邊的測試測試測試咚滋咚滋咚滋:一樣大小,各位看官有空可以嘗試看看呦!


最後再把未來會寫的Adapter丟進去就可以了!



  • 第四部:創建ViewHolder
在寫Adapter之前必須先自訂一個ViewHolder,這裡頭主要是拿來定義要放什麼控件在Adapter裡頭,我們現在創建一個Java腳本,命名為ViewHolder,準備把之後會拿來接收資料的TextView設定好!

public class ViewHolder extends RecyclerView.ViewHolder {
    public TextView mTextView;

    public ViewHolder(View itemView) {

        super(itemView);
        mTextView = (TextView)itemView.findViewById(R.id.text);

    }
}




  • 第五步:設定RecyclerView Adapter
首先我們創建一個Java檔案:暫時把它取名叫做......hmmm......就決定把你命名為Pikachu!!!!!!

創建好RecyclerViewAdapter後,有三個常用到的function需要@Override

  1. onCreateViewHolder :主要是從res/layout裡頭把要的layout放進去裡頭,我們會將之前已經新增好的recycleview_holder放入第四步創建好的ViewHolder裡頭
  2. onBindViewHolder :只要是每個item view被放進顯示用的list內時的callback,所以要依照格子位置(position)設定正確資料,待會會將資料放在我們先前創建好的TextView裡頭
  3. getItemCount:主要是決定這個RecyclerView到底有幾個item好正確的畫出scroll bar和處理滑動事件用的,等等我會使用mData.Size();  來設定item的數量。 
PS:以上這段感謝掐理大大的指證




public class RecycleViewAdapter extends RecyclerView.Adapter {
    private List mDatas;

    public RecycleViewAdapter(List mDatas){

        if(mDatas == null){
            throw new IllegalArgumentException("invaild DataBase");
        }else{
           this.mDatas = mDatas;
        }

    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {


            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycleview_holder, parent, false);
            ViewHolder vh = new ViewHolder(v);
            return vh;



    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {

        holder.mTextView.setText(mDatas.get(position));
    }


    @Override
    public int getItemCount() {
        return mDatas.size();
    }
    

}

如果都順利的完成以上步驟的話,恭喜你,初步應該能夠跑出一些畫面:
附註:記得把之前在MainActivty寫的"mRecylceView.addItemDecoration"給Mark掉,等等我們在下一步才會開始寫這個function.
現在執行的話看起來應該會像是下圖,你會發現圖案間沒有空格也沒有線,看起來沒有很好看,所以接下來,我打算寫一個非常簡單的Decoration來讓格跟格子分隔開來。







第六步:增加Decoration 首先我做了以下幾個步驟:拿取Android內的圖像資源android.R.attr.listDivider來當等等會繪製的線













  • 判定等等的StaggeredGridLayout會顯示橫向還是縱向(Vertical or Horizontal)
    1. 依照剛剛判定的結果來繪製Decoration
    1. 再從getItemOffsets來設定每個item decoration的尺算
    好,我先把程式碼奉上,稍後會再繼續講解:
    public class MarginDecoration extends RecyclerView.ItemDecoration {
      private int margin;
      private static final int[] ATTRS = new int[]{
              android.R.attr.listDivider
    
      };
      public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
      public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
      private Drawable mDivider;
    
      private int mOrientation;
    
    
      /*
      * setting Resource from attr
      * */
      public MarginDecoration(Context context,int orientation ) {
        margin = context.getResources().getDimensionPixelSize(R.dimen.item_margin);
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
    
        setOrientation(orientation);
      }
    
      public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
          throw new IllegalArgumentException("invalid orientation");
        }
        mOrientation = orientation;
      }
    
      @Override
      public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (mOrientation == VERTICAL_LIST) {
          drawVertical(c, parent);
        } else {
          drawHorizontal(c, parent);
        }
      }
    
    
      public void drawVertical(Canvas c, RecyclerView parent) {
    
    
    
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
          final View child = parent.getChildAt(i);
          final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                  .getLayoutParams();
          final int left = child.getLeft();
          final int right =left +child.getWidth();
          final int top = child.getBottom() + params.bottomMargin +margin;
          final int bottom = top + mDivider.getIntrinsicHeight() ;
          mDivider.setBounds(left, top, right, bottom);
    
    
          mDivider.draw(c);
    
        }
      }
    
      public void drawHorizontal(Canvas c, RecyclerView parent) {
    
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
          final View child = parent.getChildAt(i);
          final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                  .getLayoutParams();
          final int top = child.getTop();
          final int bottom = top + child.getHeight();
          final int left = child.getRight() + params.rightMargin;
          final int right = left + mDivider.getIntrinsicHeight();
          mDivider.setBounds(left, top, right, bottom);
          mDivider.draw(c);
        }
      }
      @Override
      public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    
        outRect.set(margin, margin, margin , margin);
    
      }
    }
    
    接下來我想要講解一下我的剛剛做的步驟: 首先我創建了一個建構子:
    MarginDecoration裏頭主要是將從android.attr裏頭拿出listDivider
    接著判定了再MainActivty傳來的方向,然在onDraw裡頭來決定如果是橫向(vertical)的話要怎麼畫,如果是直向的話要怎麼畫(horizonal)

    onDraw畫圖其實並沒有非常困難,而且這個onDraw這個方法也常常被用到(例如自訂元件)
    但onDraw這種看不見圖的方式讓我非常困擾,所以我打算在這邊分享幾個ideal,上頭寫的 drawVertical 跟 drawHorizonal裏頭的code並沒有很重要,主要是知道怎麼畫,怎麼變化比較重要!

    舉個例子好了,現在我想要畫一個直線:
    裡頭我們可以看到裏頭有一個 .setBounds(left,top,right,bottom)的function,那我到底要怎弄的?
    假想一下,這條橫線從左邊開始繪製:所以我打算將left設定為原點:0  ,繪製多少呢?繪製到右邊 10好了!那程式看起來就會是(0,10)接著我們在設定高度為1好了,那程式看起來就會是 (0,1),Android的原點在左上方,並不是一開始直覺式的認為應該是在左上方:如下圖




    所以這樣寫來應該是:mDivider.setBounds(0,0,10,1);
    如果想要再往下畫一條線,左邊跟右邊的長度不見,而高度一樣也是設定為一:
    那看起來應該像是:mDivider.setBounds(0,1,10,2);這樣看起來感覺有沒有比較簡單?


    完成了Decoration後我們就只要很簡單的在MainActivty套用上他就好了:
    mLayoutManager = new StaggeredGridLayoutManager((2), StaggeredGridLayoutManager.VERTICAL);
    看起的圖會像這樣:
    
    
    如果把程式碼改成這樣:
    mLayoutManager = new StaggeredGridLayoutManager((2), StaggeredGridLayoutManager.Horizontal);
    
    


    這樣看起來有沒有很簡單?專案已經快要完成了,接下來只要改幾地方好了:
    首先我們在drawable創建一個 xml檔案,用來替換 divider 的顏色,所以在這裡我打算命名為:divider_bg.xml
    程式碼如下:






    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
           android:shape="rectangle">
        <gradient
            android:centerColor="#7C6EFD"
            android:endColor="#FFFFFF"
            android:startColor="#FFFFFF"
            android:type="linear" />
        <size android:height="1dp"/>
    
    </shape&gt
    





    接著最後在res/values/styles.xml裏頭增加
    <item name="android:listDivider">@drawable/divider_bg</item>
    程式碼看起來會像是這樣:
    <resources>
    
        <!-- Base application theme. -->
        <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
            <!-- Customize your theme here. -->
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorAccent</item>
            <item name="android:listDivider">@drawable/divider_bg</item>
    
        </style>
    
    </resources>
    
    這樣我們的分隔線就有顏色了! 以上介紹了如何創建,如何使用,以及如何改變裝飾線(decoration)的用法,當然,RecylceView還有許許多多的用法,以及更深入的解析,就需要大家再深一步的鑽研了!
    以下是源碼:
    https://github.com/book87118/Android_Googlecalendar_Outlookcalender_Sync


    希望大家coding愉快

     Ps:如果有錯誤的地方或者有其他問題,希望大家可以留言給我呦,在我能力範圍之內會盡量幫助大家,如果這篇文章有幫助到你還麻煩在Git上面點讚呦XDDD(溜)