2016年9月27日 星期二

Android 問題:開發FireBase,Developer Services 選項太少, 沒有Cloud 等選項

使用Android studio 開發FireBse的時候,通常可以在project structure中使用Google Service建制

可是很多人project structure常常缺乏這個選項 ,當然可以自己在manifests增設權限、加libs~~

如圖:

 

解決方式




這樣就完成囉!
















2016年9月22日 星期四

Android 元件(Facebook Shimmer) 如何實現閃亮亮程式碼 文字閃爍效果

心情小品:

現在真的是人生的低潮,相信只要堅持下去 會成功的
跟大家推薦一本常常激勵我的書。

成功的路上並不擁擠 因為堅持的人不多

Facebook Shimmer


Shimmer for Facebook 推薦網站 (點我)
Facebook Shimmer JAR檔案下載位置 (點我)

本章目的:
1.在您專屬的App中 打造出獨特的風味及效果

先來看看運行的結果



讓我們開始實現這個功能吧

Step1: 請先下載FB Shimmer的jar檔 (網址在上方)



將下載的jar檔案 匯入專案


Step2: 參考一下官方的教學 範例 ,可以知道布局大概要怎麼設定


詳細位置:在Layout中 加入4個TextView 並項範例中的撰寫方式
簡單介紹:為了呈現出4種不同的效果,我們在Layout中先做出4個Shimmer View
                     這4個View的id分別為shimmer_view_container shimmer_view_container2

                                                         shimmer_view_container3 shimmer_view_container4 

                      程式碼的部分也會用底色讓各位方便區分                                                      

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
  android:background="@android:color/background_dark"
  android:orientation="vertical"
  android:paddingBottom="@dimen/activity_vertical_margin"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  tools:context="com.example.tp1606008.app_function.MainActivity">
  
  <com.facebook.shimmer.ShimmerFrameLayout
      android:id="@+id/shimmer_view_container"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content">

      <TextView
          android:id="@+id/tv_message"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="就是要帥~~!!!!"
          android:textColor="@android:color/holo_red_dark"
          android:textSize="50dp" />
  </com.facebook.shimmer.ShimmerFrameLayout>

  <com.facebook.shimmer.ShimmerFrameLayout
      android:id="@+id/shimmer_view_container2"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content">

      <TextView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="就是要帥~~!!!!"
          android:textColor="@android:color/holo_purple"
          android:textSize="50dp" />
  </com.facebook.shimmer.ShimmerFrameLayout>

  <com.facebook.shimmer.ShimmerFrameLayout
      android:id="@+id/shimmer_view_container3"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content">

      <TextView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="就是要帥~~!!!!"
          android:textColor="@android:color/holo_blue_bright"
          android:textSize="50dp" />
  </com.facebook.shimmer.ShimmerFrameLayout>

  <com.facebook.shimmer.ShimmerFrameLayout
      android:id="@+id/shimmer_view_container4"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content">
      <TextView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="就是要帥~~!!!!"
          android:textColor="@android:color/holo_green_light"
          android:textSize="50dp" />
 </com.facebook.shimmer.ShimmerFrameLayout>
</LinearLayout>

Step3: 程式碼實線的部分 新增一個Activity即可

package com.example.app_function;

import android.animation.ObjectAnimator;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import com.facebook.shimmer.ShimmerFrameLayout;

public class MainActivity extends AppCompatActivity {
   private ShimmerFrameLayout mShimmer;
   private ShimmerFrameLayout mShimmer2;
   private ShimmerFrameLayout mShimmer3;
   private ShimmerFrameLayout mShimmer4;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.shimmer_view);
       findId();
       shimmer();
   }

    //Step1:先findViewById找到View
   public void findId(){
       mShimmer  = (ShimmerFrameLayout)findViewById(R.id.shimmer_view_container);
       mShimmer2 = (ShimmerFrameLayout)findViewById(R.id.shimmer_view_container2);
       mShimmer3 = (ShimmerFrameLayout)findViewById(R.id.shimmer_view_container3);
       mShimmer4 = (ShimmerFrameLayout)findViewById(R.id.shimmer_view_container4);
   }

   public void  shimmer(){
       //Step2:開始設定mShimmer效果 (效果的參數 文章下方或官網都也有介紹)
 style="line-height: normal;">       mShimmer.setDuration(5000);
       mShimmer.setRepeatMode(ObjectAnimator.REVERSE);

        //設定mShimmer2效果
       mShimmer2.setBaseAlpha(0.1f);
       mShimmer2.setDropoff(0.1f);
       mShimmer2.setTilt(5);
       mShimmer2.setDuration(4000);

        //設定mShimmer3效果
       mShimmer3.setAngle(ShimmerFrameLayout.MaskAngle.CW_90);
       mShimmer3.setDuration(4000);

        //設定mShimmer4效果
       mShimmer4.setBaseAlpha(0);
       mShimmer4.setDuration(5000);
       mShimmer4.setDropoff(0.1f);
       mShimmer4.setIntensity(0.35f);
       mShimmer4.setMaskShape(ShimmerFrameLayout.MaskShape.RADIAL);
   }

   @Override
   protected void onStart() {
       super.onStart();
   } 

   @Override
   public void onResume() {
       super.onResume();
        //Step3:開始執行 
       mShimmer.startShimmerAnimation();
       mShimmer2.startShimmerAnimation();
       mShimmer3.startShimmerAnimation();
       mShimmer4.startShimmerAnimation();
   }

   @Override
   public void onPause() {
        //Step4:onPause的時候記得暫停
       mShimmer.stopShimmerAnimation();
       mShimmer2.stopShimmerAnimation();
       mShimmer3.stopShimmerAnimation();
       mShimmer4.stopShimmerAnimation();
       super.onPause();
   }
}



Attributes
You can control the look and pace of the effect using a number of custom attributes on the ShimmerFrameLayout tag. Alternatively, you can set these values on the layout object itself. For a comprehensive list, check out the API reference
Auto Start → setAutoStart(設定是否自動執行)
Whether to automatically start the animation when the view is shown, or not.
Base Alpha → setBaseAlpha (設定透明度)
Alpha used to render the base view i.e. the unhighlighted view over which the highlight is drawn.
Duration → setDuration(設定啟動監格的時間)
Time it takes for the highlight to move from one end of the layout to the other.
Mask Shape → setMaskShape(設定光的形狀)
Shape of the highlight mask, either with a linear or a circular gradient.
Angle → setAngle (設定閃光的傾斜角度數字有4種 分別呈現不同的方向角度)
Angle at which the highlight mask is animated, from left-to-right, top-to-bottom etc.
Dropoff → setDropoff (設定光的寬度 比例為整個Shimmer全長 做計算)
Controls the size of the fading edge of the highlight.
Intensity → setIntensity(設定被光照邊緣的透明度)
Controls the brightness of the highlight at the center
Repeat Count → setRepeatCount(設定動畫重複的次數)
Number of times of the current animation will repeat.
Repeat Delay → postDelayed(設定動畫結束 幾秒後要做的事情) ※ (Runnable r , int delayTime)
Delay after which the current animation will repeat.

這樣就完成囉 是不是很簡單呢!





















2016年7月16日 星期六

Android 元件 (Google App Engine) 建置雲端跟App互動(三) 資料轉JSON與 app溝通

 Google App Engine(三)

延續上一章節:

本章運用到了: MVC設計模式 Adapter接配線模式 BaseAdapter ListView的 ViewHolider概念

本章目的:
1. 與App進行互動 2. 完成『查詢』功能 3.『新增』、『刪除』、『變更』完全互動請至→Google App Engine(四)


將資料轉換成JSON

簡述:如何將後端的資料 轉換成能與APP溝通的模式,就是轉為JSON or XML

(A) 用途目的:方便App取得資料,首先添加 lib


(B) 撰寫程式


JAVA:MyBook
package com.example.myapplication.backend.po;

 public class MyBook {
   private long key;
   private String title;
   private String author;
   private int price;
   private long time;

 //設定好需要的變數後,剩下的用getter setter產生

    public long getKey() {
       return key;
   }
    public void setKey(long key) {
       this.key = key;
   }
    public String getTitle() {
       return title;
   }
    public void setTitle(String title) {
       this.title = title;
   }
    public String getAuthor() {
       return author;
   }
    public void setAuthor(String author) {
       this.author = author;
   }
    public int getPrice() {
       return price;
   }
    public void setPrice(int price) {
       this.price = price;
   }
    public long getTime() {
       return time;
   }
    public void setTime(long time) {
       this.time = time;
   }
}


JAVA:QueryServlet
package com.example.myapplication.backend;

import com.example..myapplication.backend.po.MyBook;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Query;
import com.google.gson.Gson;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

 public class QueryServlet extends HttpServlet {

    @Override
   public void doGet(HttpServletRequest req, HttpServletResponse resp)
           throws IOException {

        resp.setContentType("text/plain");

        DatastoreService ds = DatastoreServiceFactory.getDatastoreService();

        //查詢 資料表
       Query q = new Query("Book");

         //想辦法把取得的資料 裝進陣列中

        List<mybook> list =new ArrayList<>();

        for (Entity entity : ds.prepare(q).asIterable()) {
           //封裝資料
           MyBook myBook = new MyBook();
           myBook.setKey(entity.getKey().getId());
           myBook.setTitle(entity.getProperty("title").toString());
           myBook.setAuthor(entity.getProperty("author").toString());
           myBook.setPrice(Integer.parseInt(entity.getProperty("price").toString()));
           myBook.setTime(Long.parseLong(entity.getProperty("time").toString()));

            list.add(myBook);
       }

        Gson gson = new Gson();

        String json = gson.toJson(list);

        resp.getWriter().println(json);

        //我們需要把資料轉換成GSON的格式,方便APP下載
       Runnable r = new Runnable() {
           @Override
           public void run() {

            }
       };
   }
}


查看網頁是否成功(注意網址最後為 /query)
"你的專案名稱".appspot.com/query

成功如下圖(這是用GsonFormat呈現的)




App基本設定

簡述:後端的資料都處理好了,開始為前端做準備

Step1. Android Studio→先進行環境設定 加入網路權限
       詳細位置:Android Manifest
        程式碼:<uses-permission android:name="android.permission.INTERNET" />


Step2. Android Studio→先進行環境設定,並加入第三方程式庫

compile 'com.google.code.gson:gson:2.2.4'
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.squareup.okhttp:okhttp:2.0.0'
詳細位置:設定→Plugins→搜尋"gsonformat"
使用介紹:能自動產生 對應到該頁面json格式 的類別


Step3. Android Studio→創造類別、Layout

Class


JsonAdapter (因為json沒有Adapter,所以我們要自己做一個Adapter)
Layout
Item.xml (用來顯示每筆資料用的item)
model
Servive_My_Book(管理資料,解除耦合)
po
MyBook(用來對應到網路上json格式用), po這個資料夾命名 代表永續儲存

 Step4. 啟用google的擴充功能,使得網頁上Json的格式更清晰

             
工具都準備好了 我們開始來實現這個功能吧






App下載JSON資料
Step1 Layout
Layout:activity_main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:background="@android:color/holo_green_light"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:paddingBottom="5dp"
  android:orientation="vertical"
  android:paddingLeft="5dp"
  android:paddingRight="5dp"
  android:paddingTop="5dp"
  tools:context="com.example.app_cloud.MainActivity">
 
  <ListView
      android:layout_marginTop="20dp"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:layout_weight="2"
      android:id="@+id/listView"
      android:layout_alignBottom="@+id/webView"
      android:layout_alignParentLeft="true"
      android:layout_alignParentStart="true" />


</LinearLayout>


Layout:item
<?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="wrap_content"
  android:background="@android:color/darker_gray"
  android:orientation="vertical"
  android:weightSum="1">


  <TextView
      android:id="@+id/tv_key"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_margin="5dp"
      android:layout_weight="0.06"
      android:text="key"
      android:textAppearance="?android:attr/textAppearanceLarge" />


  <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:orientation="horizontal">


      <TextView


          android:id="@+id/tv_title"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_margin="5dp"
          android:text="title"
          android:textSize="10dp" />


      <TextView
          android:id="@+id/tv_author"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_margin="5dp"
          android:layout_weight="0.06"
          android:text="author"
          android:textSize="10dp" />


  </LinearLayout>


  <LinearLayout
      android:orientation="horizontal"
      android:layout_width="match_parent"
      android:layout_height="wrap_content">


      <TextView
          android:id="@+id/tv_price"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_margin="5dp"
          android:text="price"
          android:textSize="20dp" />


  </LinearLayout>
  <TextView
      android:id="@+id/tv_time"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_margin="5dp"
      android:text="time"
      android:textSize="10dp" />
  <View
      android:layout_width="match_parent"
      android:layout_height="10dp"
      android:background="@android:color/holo_green_light"/>


</LinearLayout>



Step2 MyBook:建立對應到網路上 對應的類別,之後打勾即可



JAVA :MyBook
package com.example.app_cloud.po;

 public class MyBook {
   private long key;
   private String title;
   private String author;
   private int price;
   private long time;

  //這邊是要給 ArrayAdapter用的

 @Override
public String toString() {
   SimpleDateFormat f = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss E");

   String data = String.format
   ("書名:%s\tkey:%d\n作者:%s\t價格:%d\n上架時間:%s"
   , title, key, author, price, f.format(new Date(time)));
   return data;
}

   public long getKey() {
       return key;
   }
    public void setKey(long key) {
       this.key = key;
   }
    public String getTitle() {
       return title;
   }
    public void setTitle(String title) {
       this.title = title;
   }

    public String getAuthor() {
       return author;
   }
    public void setAuthor(String author) {
       this.author = author;
   }
    public int getPrice() {
       return price;
   }
    public void setPrice(int price) {
       this.price = price;
   }
    public long getTime() {
       return time;
   }
    public void setTime(long time) {
       this.time = time;
   }
}


Step3 Service_My_Book:(model)

JAVA :Service_My_Book
package com.example.app_cloud.model;

 import com.example.app_cloud.po.MyBook;
import java.text.SimpleDateFormat;
import java.util.Date;

 public class Service_My_Book {

    //JSON中每一筆資料都是 MyBook,所以用型別是MyBook 集合去裝
   private MyBook[] service_myBooks;
  
   //建構子
   public Service_My_Book(MyBook[] myBooks) {
       this.service_myBooks = myBooks;
   }

    //需要資料就透過 Service_My_Book,不要伸手到Mybook中拿
   public String getKey(int postion) {
       return "書籤代碼= " + String.valueOf(service_myBooks[postion].getKey());
   }

    public String getTitle(int postion) {
       return "標題= " + service_myBooks[postion].getTitle();
   }

    public String getAuthor(int postion) {
       return "作者名稱= " + service_myBooks[postion].getAuthor();
   }

    public String getPrice(int postion) {
       return "販售的金額= " 
              + String.valueOf(service_myBooks[postion].getPrice());
   }

    public String getTime(int postion) {
//創立一個format的物件
       SimpleDateFormat formatdate; 
       formatdate= new SimpleDateFormat("yyyy/MM/dd HH:mm:ss E");

 //先取得時間
       Long time = service_myBooks[postion].getTime();

        String data = String.format(formatdate.format(new Date(time)));

        return "上架時間= " + data;
   }
}


Step4 JsonAdapter:(Adapter)

JAVA :JasonAdapter
package com.example.app_cloud;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.example.app_cloud.model.Service_My_Book;
import com.example.app_cloud.myapp.MyApp;
import com.example.app_cloud.po.MyBook;

 public class JsonAdapter extends BaseAdapter {
   private Context context;
   private MyBook[] myBooks;
   private LayoutInflater layoutInflater;

    //建構子
   public JsonAdapter(Context context, MyBook[] myBooks) {
       this.context = context;
       this.myBooks = myBooks;
   }
    //size
   @Override
   public int getCount() {
       return myBooks.length;
   }
//item
   @Override
   public Object getItem(int i) {
       return myBooks[i];
   }
    //id
   @Override
   public long getItemId(int i) {
       return i;
   }

    @Override
   public View getView(int postion, View view, ViewGroup viewGroup) {
       ViewHolder viewHolder;

        //view == null
       if (view == null) {

            //如果view == null,我才創建一個新的view
            //避免下載一次 就創造一個View出來
           view = layoutInflater.from(context).inflate(R.layout.item, null);

            //如果view == null,我再findViewByid
           viewHolder = new ViewHolder();

        viewHolder.m_tv_time = (TextView) view.findViewById(R.id.tv_time);
        viewHolder.m_tv_title = (TextView) view.findViewById(R.id.tv_title);
        viewHolder.m_tv_author = (TextView) view.findViewById(R.id.tv_author);
        viewHolder.m_tv_price = (TextView) view.findViewById(R.id.tv_price);
        viewHolder.m_tv_key = (TextView) view.findViewById(R.id.tv_key);

            // 把viewHolider裝進標籤
           view.setTag(viewHolder);

        } else {
           //如果 !=null,我就標籤的Hoviewlider 取出
           viewHolder = (ViewHolder) view.getTag();
        }

       //setter 封裝 Service_my_book
       if(service_my_book==null) {
            service_my_book = new Service_My_Book(myBooks);
       }

        //取得model 並索取資料
    Service_My_Book service = MyApp.getService_my_book();

    viewHolder.m_tv_time.setText(service.getTime(postion));
    viewHolder.m_tv_title.setText(service.getTitle(postion));
    viewHolder.m_tv_author.setText(service.getAuthor(postion));
    viewHolder.m_tv_price.setText(service.getPrice(postion));
    viewHolder.m_tv_key.setText(service.getKey(postion));
        return view;
   }

        //Holder (避免每下載一次就findviewbyId,有效利用ListView緩存機制)
   public class ViewHolder {
       private TextView m_tv_time;
       private TextView m_tv_title;
       private TextView m_tv_author;
       private TextView m_tv_price;
       private TextView m_tv_key;
   }
}


Step5 MainActivity:(main_activity)

JAVA :MainActivity
package com.example.app_cloud;

import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.ListView;
import com.example.app_cloud.po.MyBook;
import com.google.gson.Gson;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import java.io.IOException;

 public class MainActivity extends AppCompatActivity {
   private Context context;
   private ListView listView;
   private MyBook[] collection_mybook;
   private ArrayAdapter arrayadapter;
   private BaseAdapter baseAdapter;

    //非常爛的方法 測試用
   private Handler handler=new Handler();
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       finID();

        Runnable r = new Runnable() {
           @Override
           public void run() {
               new MyThread().start();
               handler.postDelayed(this,2000);
           }
       };

  //不太好的方法,暫時測試用,下一章節再補充
       handler =new Handler();
       handler.post(r);
   }

    public void finID() {
        context = this;
       listView = (ListView) findViewById(R.id.listView);
   }

    public class MyThread extends Thread {
       String myjson;
       // step1.這段程式碼由OKhttp所提供
       // 分析 http 資料結構標準語法
       // Url是再把網址放入 這時候會發送 request方法給伺服器
       // 伺服器根據你要資料把資料用 response 回傳給你
      

        OkHttpClient client = new OkHttpClient();

        String run(String url) throws IOException {
           Request request = new Request.Builder()
                   .url(url)
                   .build();

            Response response = client.newCall(request).execute();
           return response.body().string();
       }

        //step3:創造出step2所要的工作內容
       Runnable r = new Runnable() {
           @Override
           public void run() {
               Gson gson = new Gson();

              collection_mybook=gson.fromJson(myjson,MyBook[].class);

                    //第1種 Adapter
               arrayadapter = new ArrayAdapter(
context
, android.R.layout.simple_expandable_list_item_1
, collection_mybook);

                    //第2種 Adapter
               baseAdapter =new JsonAdapter(context,collection_mybook);

                    //想要用哪一個就自己set
               listView.setAdapter(baseAdapter);
               arrayadapter.notifyDataSetChanged();
           }
       };

   //step2.
       // 運用OkHttp run的方法中,放入我們要讀取JSON的網址
       // 放入後再另一個排程, runOnUiThread(r),
       // runOnUiThread 就好像為了要持續改變 環境UI的方法
       // r就是工作的內容,這時候就要在new Runnable()出來。(上面)


       @Override
       public void run() {
           try {
               myjson = run("http://cloud1-1356.appspot.com/query");
               runOnUiThread(r);
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }
}




我們來看一下實機 操作結果吧