2015年12月30日 星期三

Android-音量控制

參考資訊 : [多媒体] 音量调节
參考資訊 : [索引汇总帖] 【eoeAndroid社区索引】android设备功能之音量教程实例汇总
參考資訊 : How to capture key events from bluetooth headset with android

Android-MediaPlayer 音樂播放


import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;

public class MainActivity extends Activity {

    private MediaPlayer mediaPlayer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        try {
            mediaPlayer = new MediaPlayer();
            mediaPlayer = MediaPlayer.create(this, R.raw.lucky_star);
            mediaPlayer.start();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        }

    }
}


參考資訊 : 使用多媒體播放器(MediaPlayer)播放MP3音樂
參考資訊 : 音樂播放器(MediaPlayer)

2015年12月26日 星期六

Android-ListView 和 CheckBox 問題

不知道是否有大大遇過類似以下情況 :
比如有 30 筆資料
點選第 1 筆後
竟然在 2X 筆突然出現被勾選的狀態
 If you check first checkbox and slide down, Actually the twentieth checkbox
was checked.

解決辦法 :
1 . 把被點選的位置記錄下來
2 . 生成 CheckBox 時去判斷是否要被勾選

import java.util.HashMap;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.TextView;

public class MyAdapter extends BaseAdapter{

    private HashMap<Integer, Boolean> hashMap = new HashMap<Integer,Boolean>();
    .
    .
    .
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Elements elements = null;  

        if (convertView == null) {  
            .
            .
            .
        } else {  
            .
            .
            .
        }  
        
        elements.checkBox1.setOnCheckedChangeListener(new OnCheckedChangeListener(){

            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if(isChecked)
                {  
                    hashMap.put(position, isChecked);
                }else{
                    hashMap.remove(position);
                }                
            }});
        elements.checkBox1.setChecked(hashMap.get(position)==null? false : true);
        
        return convertView; 
    }

}



參考資料 : ListView点击checkbox其他checkbox也被同时选中的问题

Android-Copy project and rename package

If you  want to create new project form the project ( A -> A' )
You must :
1 . rename package name
2 . AndroidManifest registered activity、service、package... must change package name
3 .Change def packageName on build.gradle
4 . rebuild project

2015年12月21日 星期一

Android-文字 轉 語音 TTS


import java.util.Locale;

import android.content.Context;
import android.speech.tts.TextToSpeech;

public class MyTTS implements TextToSpeech.OnInitListener{

    private Context context;
    private TextToSpeech mTextToSpeech = null;
    private boolean isLoaded = false;

    // initTTS
    public MyTTS(Context context){
        try {
            this.context = context;
            this.mTextToSpeech = new TextToSpeech(context, this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onInit(int status) {
        if (status == TextToSpeech.SUCCESS) 
        {
            mTextToSpeech.setLanguage(Locale.US);
            isLoaded = true;
        } 
        else 
        {
            isLoaded = false;
        }        
    }

    // stop action about tts
    public void close(){
        if(mTextToSpeech != null)
        {
            mTextToSpeech.stop(); 
            mTextToSpeech.shutdown();
        }
    }
    
    public void queueSpeak(String text) {
        if (isLoaded)
            mTextToSpeech.speak(text, TextToSpeech.QUEUE_ADD, null);
    }

    public void flushSpeak(String text) {
        if (isLoaded)
            mTextToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, null);
    }
    
}


參考資料 : Android TextToSpeech Example

Android-防丟器為例













在還沒開始前,想必大家對 Bluetooth LE Scanner 不陌生吧
在開始編寫前可以先拿上方的應用程式操作看看




檢查手機版本和有無開啟藍芽
/** 初始化 */
public boolean initialize() {
    // For API level 18 and above, get a reference to BluetoothAdapter through
    // BluetoothManager.
    if (bluetoothManager == null) {
        bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        if (bluetoothManager == null) {
            Log.e(TAG, "Unable to initialize BluetoothManager.");
            return false;
        }
    }
    bluetoothAdapter = bluetoothManager.getAdapter();
    if (bluetoothAdapter == null) {
        Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
        return false;
    }
    return true;
}

裝置連線
/** 連線至藍芽裝置 */
public boolean connect(final String address) {
    if (bluetoothAdapter == null || address == null) {
        Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
        return false;
    }
    // Previously connected device.  Try to reconnect.
    if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
            && bluetoothGatt != null) {
        Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
        if (bluetoothGatt.connect()) {
            mConnectionState = STATE_CONNECTING;
            return true;
        } else {
            return false;
        }
    }
    final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
    if (device == null) {
        Log.w(TAG, "Device not found.  Unable to connect.");
        return false;
    }
    bluetoothGatt = device.connectGatt(this, false, mGattCallback);
    Log.d(TAG, "Trying to create a new connection.");
    mBluetoothDeviceAddress = address;
    mConnectionState = STATE_CONNECTING;
    return true;
}

與裝置離線
public void disconnect() {
    if (bluetoothAdapter == null || bluetoothGatt == null) {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    bluetoothGatt.disconnect();
}

關閉藍芽
public void close() {
    if (bluetoothGatt == null) {
        return;
    }
    Log.w(TAG, "mBluetoothGatt closed");
    mBluetoothDeviceAddress = null;
    bluetoothGatt.close();
    bluetoothGatt = null;
}

連線後的回調
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            mConnectionState = STATE_CONNECTED;
            broadcastUpdate(intentAction);
            Log.i(TAG, "Connected to GATT server.");
            Log.i(TAG, "Attempting to start service discovery:" +
                bluetoothGatt.discoverServices());
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            mConnectionState = STATE_DISCONNECTED;
            Log.i(TAG, "Disconnected from GATT server.");
            disconnect();
            close();
        }
        broadcastUpdate(intentAction);
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
                enableNotification();
        } else {
            Log.w(TAG, "onServicesDiscovered received: " + status);
        }
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt,
            BluetoothGattCharacteristic characteristic,
            int status) {
        Log.w(TAG, "onCharacteristicRead received: " + characteristic.getValue());
        
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt,
            BluetoothGattCharacteristic characteristic) {
        Log.w(TAG, "onCharacteristicChanged received: " + characteristic.getValue());
    }

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        super.onCharacteristicWrite(gatt, characteristic, status);
        Log.w(TAG, "onCharacteristicWrite received: ");
    }
};

新增服務提醒
public void enableNotification()
{
    // is support SERVICE 
    BluetoothGattService Service = bluetoothGatt.getService(SERVICE_UUID);
    if (Service == null) {
        Log.w(TAG, "service not found!");
        return;
    }
    // is support Characteristic
    BluetoothGattCharacteristic Char = Service.getCharacteristic(CHARACTERISTIC_UUID);
    if (Char == null) {
        Log.w(TAG, "Tx charateristic not found!");
        return;
    }
    bluetoothGatt.setCharacteristicNotification(Char,true);
    BluetoothGattDescriptor descriptor = Char.getDescriptor(CCCD);
    final byte[] ENABLE_NOTIFICATION_VALUE__ = {0x01};
    descriptor.setValue(ENABLE_NOTIFICATION_VALUE__);
    bluetoothGatt.writeDescriptor(descriptor);
}

取得所有服務
for(BluetoothGattService bluetoothGattService: gatt.getServices())
{
    Log.w(TAG, "----------------------------------");
    Log.w(TAG, "BluetoothGattService : " + bluetoothGattService.getUuid() );
    for(BluetoothGattCharacteristic bluetoothGattCharacteristic: bluetoothGattService.getCharacteristics())
    {
        Log.w(TAG, "BluetoothGattCharacteristic : " + bluetoothGattCharacteristic.getUuid() );
        for(BluetoothGattDescriptor bluetoothGattDescriptro: bluetoothGattCharacteristic.getDescriptors())
        {
            Log.w(TAG, "BluetoothGattDescriptor : " + bluetoothGattDescriptro.getUuid());
            if(bluetoothGattDescriptro.getValue() != null)
                Log.w(TAG, "BluetoothGattDescriptor : " + bluetoothGattDescriptro.getValue());                
            
        }
    }
}

參考資料 : AndroidとBLE
參考資料 : NordicSemiconductor/Android-nRF-Toolbox
參考資料 : BLE Health Devices: First Steps with Android
參考資料 : Bluetooth Low Energy (or LE) is a very cool technology.
參考資料 : SensorTag User Guide_Android

2015年12月18日 星期五

Android-判斷手機 網路 藍芽 狀態

手機裝置狀態
Check Cellphone State : Bluetooth、NetWork

權限
<!-- 藍芽 -->
<uses-permission android:name="android.permission.BLUETOOTH" />

<!-- 網路 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />


import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
/**
 * 裝置狀態
 */
public class DeviceState {

    /** 網路是否連線 */
    public static boolean isNetWorkConnection(Context context){

        boolean isConnection = false;

        ConnectivityManager mConnectivityManager = 
                (ConnectivityManager) context.getApplicationContext()
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();

        if(mNetworkInfo != null)
            if(mNetworkInfo.isConnected())
                isConnection = true;

        return isConnection;
    }

    /** 裝置是否支援低功率藍芽 */
    public static boolean isSupportBle(){

        boolean isSupport = false;

        BluetoothAdapter mBluetoothAdapter = 
                BluetoothAdapter.getDefaultAdapter();

        if (mBluetoothAdapter != null) 
            isSupport = true;

        return isSupport;
    }

}

參考資料 : 【Android】檢查網路連線狀態 Connectivity Network Active State

歡迎轉載,請註明出處。

2015年12月17日 星期四

Android-RecyclerView Sample 範例 Kotlin

更新日期:20189116 新增範例( kotlin )

雖然有 ListView 和 GridView 顯示資訊
但還要另外寫選配器
Google 又另外推出 RecyclerView
聞說效率比前 2 個 View 效率更高
話不多說

先來個簡單流程說明吧
1 . 首先設計要顯示表格的畫面內容
2 . 將剛剛設計的內容格式放置選配器內
3 . 將資料放置選配器中
4 . 依照不同的需求顯示

直接來個範例

1 . build.gradle 先匯入以下
compile 'com.android.support:cardview-v7:26.1.0'
compile 'com.android.support:recyclerview-v7:26.1.0'

2 . 新增元素介面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

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


        <ImageView
            android:id="@+id/item_record_1_img"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            app:srcCompat="@mipmap/ic_launcher" />

        <TextView
            android:id="@+id/item_record_1_text"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="TextView" />
    </LinearLayout>

</LinearLayout>

3 . 設定頁面參數
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.light.blue.counter.R;

public class MyViewHolder_1 extends RecyclerView.ViewHolder {

    public ImageView mImageView;
    public TextView mTextView;

    public MyViewHolder_1(View view) {
        super(view);
        // 添加預設的模組
        mImageView = view.findViewById(R.id.item_record_1_img);
        mTextView = view.findViewById(R.id.item_record_1_text);
    }
}


4 . 設定選配器
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.light.blue.adpater.view.MyViewHolder_1;
import com.light.blue.counter.R;

import java.util.List;

public class MyAdapter_1 extends RecyclerView.Adapter<MyViewHolder_1> {

    private List<String> mList;

    public MyAdapter_1(List<String> list) {
        mList = list;
    }

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

    @Override
    public void onBindViewHolder(MyViewHolder_1 holder, int position) {
        holder.mTextView.setText(mList.get(position).toString());
    }

    @Override
    public MyViewHolder_1 onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        View view = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.item_record_1, viewGroup, false);

        return new MyViewHolder_1(view);
    }

}

5 . 在主畫面宣告
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycleView_recyclerList"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical">

    </android.support.v7.widget.RecyclerView>

</android.support.constraint.ConstraintLayout>

6 .
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import com.light.blue.adpater.MyAdapter_1
import com.light.blue.sql.until.MyDatabaseHandler
import kotlinx.android.synthetic.main.activity_recycleview.*
import java.util.*

class RecycleListActivity : AppCompatActivity() {

    private lateinit var myDatabaseHandler: MyDatabaseHandler;

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_recycleview)

        myDatabaseHandler = MyDatabaseHandler(this);

        var listStr = ArrayList<String>();
        listStr.add("小明");
        listStr.add("小王");
        listStr.add("小黃");
        listStr.add("小黑");
        listStr.add("小白");

        recycleView_recyclerList.layoutManager = LinearLayoutManager(this);
        // recycleView_recyclerList.layoutManager = GridLayoutManager(this, 3);
        recycleView_recyclerList.adapter = MyAdapter_1(listStr);

    }

}


( LinearLayoutManager )






















(GridLayoutManager)




參考資料 : Creating Lists with RecyclerView in Android
參考資料 : RecyclerView使用详解(一)

2015年12月16日 星期三

Android-Create New Project

想必各位大大心目中有一些想實踐的App
但要如何開始?
建議 : 不要一想到就直接開始動工
不然後繼無力就很容易失去原有的方向

不做申論只需簡單問自己
然後以最簡短的一句話寫在紙上

首先,先分析 3W1H !!!
1 . Why?
為甚麼要寫此 App?

2 . Who?
此 App 使用對象為?

3 . What?
未來展望?想要的目的?

4 . How?
要如何做?

如果此問題都能回答出來
因該就算是踏出第一步了
--------------------------------------------------
接下來就是實作分析
1 . Functional Map
2 . Flow
3 . UI design

Android-水波紋特效 ripple

20170311 新增:ImageButton has ripple effect

Google 在 5.0 有新增一些特效
最明顯的例子 : 按鍵有水波紋特效
至於能不能讓使用者接受
只能見仁見智了

1 . 在專案內 新增 drawable-v21
2 . 用 ripple 包覆元素
3 . 物件套用剛剛客製的 drawable

注意 : 最小 SDK 版本為 21

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/white">
    <item android:drawable="@drawable/ic_l"/>
</ripple>

參考資訊 : Lollipop RippleDrawable vs Selector for Pre-Lollipop
參考資訊 : How to Add Ripple Effect/Animation to a Android Button
參考資料:Apply Material Design Touch Ripple to ImageButton?

--------------------------------------------------------
如果手機版本比較低可以參考 :
參考資訊 : material-ripple
參考資訊 : RippleEffect
參考資訊 : 內有連結

2015年12月14日 星期一

Android-Take Photo and save file

1 . 新增權限
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

2 . 設定 RequestCode
public static final int TAKE_PHOTO_IMAGE_CODE = 1234;

3 . 開啟相機
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
startActivityForResult(intent, TAKE_PHOTO_IMAGE_CODE);    

4 . 接收拍照結果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == TAKE_PHOTO_IMAGE_CODE) 
    {
        //Check if data in not null 
        if (data != null)
        {
            Bitmap bitmap = (Bitmap)data.getExtras().get("data");
            imageView.setImageBitmap(bitmap);
        
        }
    }
}

5 . 如果要有儲存照片功能
public static final String FileName = "Hello";

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    //Check that request code matches ours:
    if (requestCode == TAKE_PHOTO_IMAGE_CODE) 
    {
        // 建立資料夾
        File imageStorageFolder = 
            new File(Environment.getExternalStorageDirectory()+File.separator + FileName);
            
        if (!imageStorageFolder.exists())
        {
            imageStorageFolder.mkdirs();
            Log.d(TAG , "Folder created at: "+imageStorageFolder.toString());
        }
        //Check if data in not null 
        if (data != null)
        {
            String imageName = "image";
            String fileNameExtension = ".jpg";
            File sdCard = Environment.getExternalStorageDirectory();
            String strImageStorageFolder = File.separator + FileName + File.separator;
            File destinationFile = 
                new File(sdCard, strImageStorageFolder + imageName + fileNameExtension);
            Log.d(TAG, "the destination for image file is: " + destinationFile );
            if (data.getExtras() != null)
            {
                Bitmap bitmap = (Bitmap)data.getExtras().get("data");
                //判斷照片為橫向或者為直向,並進入ScalePic判斷圖片是否要進行縮放
                if(bitmap.getWidth()>bitmap.getHeight())
                    ScalePic(bitmap,mPhone.heightPixels);
                else 
                    ScalePic(bitmap,mPhone.widthPixels);
                try
                {
                    FileOutputStream out = new FileOutputStream(destinationFile);
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
                    out.flush();
                    out.close();
                } 
                catch (Exception e) 
                {
                    Log.e(TAG, "ERROR:" + e.toString());
                }
                
            }
        }
    }
}

private void ScalePic(Bitmap bitmap,int phone)
{
    //縮放比例預設為1
    float mScale = 1 ;
    //如果圖片寬度大於手機寬度則進行縮放,否則直接將圖片放入ImageView內
    if(bitmap.getWidth() > phone )
    {
        //判斷縮放比例
        mScale = (float)phone/(float)bitmap.getWidth();
        Matrix mMat = new Matrix() ;
        mMat.setScale(mScale, mScale);
        Bitmap mScaleBitmap = Bitmap.createBitmap(bitmap,
                0,
                0,
                bitmap.getWidth(),
                bitmap.getHeight(),
                mMat,
                false);
        imageView.setImageBitmap(mScaleBitmap);
    }
    else imageView.setImageBitmap(bitmap);
}


參考資料 : Android : Use Camera Activity for Thumbnail and FullSize Image
參考資料 : 【Android】開啟相機-照相功能與相簿-相片集並讀取相片匯入至程式

歡迎轉載,請註明出處。

Java-時間 日期 增減


DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.DATE, 3);
String date = dateFormat.format(calendar.getTime());
System.out.println(date);

歡迎轉載,請註明出處。

2015年12月13日 星期日

Android-廣告 滑動頁面 ViewPager + PagerAdapter(一)

更新日期 : 20160116 新增索引點
更新日期 : 20160217 切換頁面監聽更改

相信各位在安裝 App 時候一定有引導教學左右滑動頁面
經過測試後
其實跟我們常用的 ListView 、GridView 大同小異
如果對 BaseAdapter 熟悉的朋友
一定很快就能上手
注意 : 要使用此功能,必須匯入 V4 包

順序
1 . 抓出畫面共同點!? ( 有些App會在最後一頁有 Button ,可以先隱藏 )
2 . 設計畫面UI
3 . 設計選配器
4 . 將 選配器放進 ViewPager ( 像 ListView )內

這次的 Sample 以單純圖片顯示

1 . cell_pager_image.xml
<?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="match_parent">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/imageView"
        android:scaleType="fitXY"
        android:layout_gravity="right" />
</LinearLayout>

2 . MyPagerAdapter.java
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;

import java.util.List;

public class MyPagerAdapter extends PagerAdapter {
    
    private Context context;
    private List<Integer> list;
    private LayoutInflater inflater;

    public MyPagerAdapter(Context context, List<Integer> list) {
        this.context = context;
        this.list = list;
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        inflater = (LayoutInflater) 
            context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            
        // 佈局
        View itemView = inflater
            .inflate(R.layout.cell_pager_image, container, false);

        // 佈局元件內容
        ImageView imageView = (ImageView)itemView.findViewById(R.id.imageView);
        imageView.setImageResource(list.get(position));

        // 加載
        (container).addView(itemView);

        return itemView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        // LinearLayout 是以 cell_pager_image 主體變更
        container.removeView((LinearLayout) object);
    }

}

3 . activity_main.xml (將設定好的選配器設置到 ViewPager 上
<?xml version="1.0" encoding="utf-8"?>
<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">

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </android.support.v4.view.ViewPager>

</RelativeLayout>


4 . MainActivity.java
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.ViewPager;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity implements ViewPager.OnPageChangeListener{

    ViewPager viewPager;
    MyPagerAdapter myPagerAdapter;

    // 在專案內放的測試圖
    private static final int[] pictures = { R.drawable.icon_01,
            R.drawable.icon_02, R.drawable.icon_03 };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        viewPager = (ViewPager) findViewById(R.id.viewpager);

        List<Integer> list = new ArrayList<Integer>();
        for(int i : pictures){
         list.add(i);
        }

        myPagerAdapter = new MyPagerAdapter(this, list);
        viewPager.setAdapter(myPagerAdapter);

    }

}


如果要對頁面設定監聽器
ViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        }

        @Override
        public void onPageSelected(int position) {

        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });

參考資料 : Viewpager setonpagechangelistener deprecated

有關於底下原點,必須要在MainActivity設定
簡單的說必須配合監聽換頁
如果有時間會在寫一些 Sample


參考資料 : Android ViewPager Gallery Images and Texts Tutorial
參考資料 : Android通用圆点指示器——android-Universal-CircleIndicator
參考資料 : ViewPagerIndicator 分頁指示器

參考資料 : VIEW PAGER WITH CIRCULAR INDICATOR (WITHOUT ANY LIBRARY)

歡迎轉載,請註明出處。

2015年12月7日 星期一

Android-DatePickerDialog 範例 Sample

官方日期選擇器

1 . 實作 DatePickerDialog
Calendar calendar0 = Calendar.getInstance();
DatePickerDialog dialog0 = new DatePickerDialog(this, this,
        calendar0.get(Calendar.YEAR), calendar0.get(Calendar.MONTH),
        calendar0.get(Calendar.DAY_OF_MONTH));
dialog0.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int which) {
        dialog.cancel();
    }
});
dialog0.show();

2 . 實作 DatePickerDialog
....implements implements DatePickerDialog.OnDateSetListener{ ... }

3 . 接回參數
// CallBack
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear,
                      int dayOfMonth) {
    SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
    Log.d("DateTest","Date : " + format.format(new Date()));
    
}

歡迎轉載,請註明出處。

2015年11月22日 星期日

Android-AndroidStudio GCM

更新日期 : 20160318_新增註冊ID內容

使用 AndroidStudio 為 App 添加 GCM( Google Cloud Message )
想必大大對 GMC 不陌生吧
如果不知道的沒關係
以下舉例您可能馬上就知道
1 . 安裝應用 App 三不五時就會收到擾人的通知
2 . 安裝應用 遊戲 後,突然收到開戰通知!!!

這些大多都是 推播 ( 以GCM為例 )!!!
為啥要以 GCM 為範例?
其實微軟、亞馬遜也有提供
但是以大眾來講 GCM 是最普及的

進入重點

是如何動作的
1 . 開發者向 Google 申請序號
2 . 在 App 內放置序號並且實作 GCM
3 . 成功之後會回傳 Token
4 . 開發者將 Token 傳送到指定位置存放
5 . 開發者將指定 Token 配合並發送,使用者手機就可收到訊息

步驟
1 . 申請專案序號
連結 : GoogleDevelopersConsole

















參考資料 : android-gcm-tool
參考資料 : Push Notification using GCM in Android Studio | Google Cloud Messaging

2015年11月21日 星期六

Java-日期/時間 格式轉換

更新日期 : 20151212 ( 新增流程 )

流程 :
1 . 資料來源
2 . 來源格式
3 . 目的格式
4 . 將來資料來源轉換成 Date (parse)
5 . 將 來源Date 套用在 目的格式 (format)
6 . 轉換成字串

try {
    // 1 ( 二選一 )
    // 自定義
    String date = "2015/12/12 07:07:07";
    // 系統時間
    DateFormat dateFormat_1 = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    String date = dateFormat_1.format(new Date());
    System.out.println(date);
    // 2
    DateFormat dateFormat_1 = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    // 3
    DateFormat dateFormat_2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    // 4 
    Date date_org = dateFormat_1.parse(date);
    // 5 . 6
    String myDate = dateFormat_2.format(date_org);
    System.out.println(myDate);
} catch (ParseException e) {
    e.printStackTrace();
}


歡迎轉載,請註明出處。

2015年11月18日 星期三

Android-左右滑動圖片 Image ViewPager

參考資料 : 使用ViewPager实现左右滑动效果
參考資料 : Android 基于ImageSwitcher实现的左右切换图片
參考資料 : Android Image Slideshow using ViewPager with PagerAdapter
( 有成功,但是沒壓縮造成反應遲緩,無引導點 )
若改無限制
@Override
public int getCount() {
   return mResources.length;
}
圖片來源記得要取餘數,不然會 Exception
參考資料 : Android UI设计——ViewPager中设置底部显示圆点焦点(二)
( 引導點 )
參考資料 : 用ViewPager实现欢迎引导页面
( 帶有引導點,可動態增加圖片 )

2015年11月13日 星期五

Android-Google Ads 廣告置入 範例 Sample

20170311 新增:整頁廣告

想必大家都聽過寫 App 然後靠廣告賺錢
那要如何把廣告植入在我們的心血?
其實沒有我們想像的困難

此 Sample 是以 AndroidStudio 版本為主 : 連結
如果大大是以 Eclipse 開發 請參考 : 連結

雖然網路以上有流程
但只需要自己所要的部分

1 . 在 Gradle 內加入
compile 'com.google.android.gms:play-services-ads:7.8.0'

2 . 在要呈現的版面新增元件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:ads="http://schemas.android.com/apk/res-auto"
              .
              .

              <com.google.android.gms.ads.AdView
                android:id="@+id/adView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_alignParentBottom="true"
                ads:adSize="BANNER"
                ads:adUnitId="@string/banner_ad_unit_id">
                </com.google.android.gms.ads.AdView>
</LinearLayout>

3 . 宣告元件
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
        
        AdView mAdView = (AdView) findViewById(R.id.adView);
        AdRequest adRequest = new AdRequest.Builder().build();
        mAdView.loadAd(adRequest);
    .
    .
}

4 . 將 AdMob 給的序號添加在 String.xml
序號來源 以下有教學
<resources>
    <string name="banner_ad_unit_id">ca-app-pub-111222333444555666/555444111</string>
</resources>

----------------------------------------------------------------
關於廣告帳號註冊
1 . 申請 Ads 帳號
首頁
https://apps.admob.com/#home
註冊Key
https://apps.admob.com/#monetize/pubcontrols:urls

參考資料: Interstitial Ads
參考資料:Interstitial Ads 無法顯示的問題
歡迎轉載,請註明出處。

Android-Parse Push ParsePushBroadcastReceiver ( 三 )

在前面兩個章節有介紹如何 :
  1. 初始化 Parse 
  2. 接收 Parse Push Message
  3. 發送 Push Message
這次我們要做第二點的延伸
延伸?

簡單的說,Parse SDK 有預設
但我們想自己做 Design 這樣可以嗎??
Notification Icon、Notification Message  ...etc.

當然是可以的
1 . extends ParsePushBroadcastReceiver
( ex : is ParsePushBroadcastReceiver, isn't ParseBroadcastReceiver )
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.parse.ParsePushBroadcastReceiver;

public class MyParseReceiver extends ParsePushBroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Log.d("MyParseReceiver", "action:" + action);
    }
}

You can add key in onReciver

- String strChannel = intent.getExtras().getString("com.parse.Channel");
- String strData = intent.getExtras().getString("com.parse.Data");


2 . 將原先的註冊的 Receiver 更改
<receiver
    android:name="com.parse.ParsePushBroadcastReceiver"
    android:exported="false" >
    <intent-filter>
        <action android:name="com.parse.push.intent.RECEIVE" />
        <action android:name="com.parse.push.intent.OPEN" />
        <action android:name="com.parse.push.intent.DELETE" />
    </intent-filter>
</receiver>

( 更改成剛剛繼承的 Receiver )
<receiver
    android:name="MyParseReceiver"
    android:exported="false" >
        <intent-filter>
        <action android:name="com.parse.push.intent.RECEIVE" />
        <action android:name="com.parse.push.intent.OPEN" />
        <action android:name="com.parse.push.intent.DELETE" />
    </intent-filter>
</receiver>

3 . 如果覺得原生的 Notification 太單調,也可以改成自己的
// 將提醒功能取消
@Override
protected Notification getNotification(Context context, Intent intent) {
    return null;
}

private void myNotification(...){
    Notification...
}


歡迎轉載,請註明出處。

2015年11月9日 星期一

Android-Weather API

World Weather Online
Link : http://www.worldweatheronline.com/





























OpenWeatherMap
Link : http://openweathermap.org/




























台灣氣象資訊
雖然有資料,但送出格式有待確認

Java-Synchronized 同步 用法 執行

想必大家都聽過 Synchronized 同步 之類的
但聽過這麼多
但是到底是啥東西??
簡單的說 : 你要使用此物件、方法,你就是要
排隊!!!
排隊!!!
排隊!!!

但是前者結束,後者要搶!!!

為啥要排隊?不是直接使用就好了嗎??
當然也行,但在銀行的例子就不可行
在同一時間只能一個人做領錢的動作
直到確認把錢交至手上為止
下一個人才能領錢或存錢

那寫哪種程式比較常遇到 ?
檔案接收完整性、資料存取之類

以下圖示針對銀行例子做圖解
( 左 : 同時領錢 , 右 : 排隊領錢 )














以下是用範例呈現 :

1 . 不使用 Synchonized
 多次執行,結果皆不同。














2 . 使用 Synchonized
多次執行,結果相同。














以下是範例程式碼 : ( 請自行更改 synchronized 需求 )
Test.java
public class Test {

    public static void main(String[] args) {

        TestThread.Threading("1");
        TestThread.Threading("2");
        
    }

}



TestThread.java
public class TestThread {
    
    public static void Threading(String message){
        new Threading(message).start();
    }
    
    public static class Threading extends Thread { 

        private String _strMessage;

        Threading(String strMessage){
            this._strMessage = strMessage;
        }
        public void run() { 
            // synchronized (TestThread.class) {
                try {
                    for(int i=0; i < 5; i++){
                        Thread.sleep(1000);
                        System.out.println(_strMessage + " : " + i);
                    }
                } catch (InterruptedException e) {
                }
            // }
        }
    }
}



注意 : 

因為線程的執行本身就是不確定的
加了 Synchronizes 只能確保這段程式碼和參數
並不能保證程序的執行順序!!!

如果真的要有順序,先暫時用 Sleep 頂替。

參考資料 : [Java] Synchronized 心得
參考資料 : JAVA筆記-synchronized 同步物件資料,避免Race Condition

歡迎轉載,請註明出處。

2015年11月5日 星期四

Android-查詢背景所有的服務

參考資料 : android如何判断后台一个Service是否在运行

Android-GreenDAO 的基本使用方式 可當小型 資料庫 - 3

如果有需要再新增欄位怎麼辦?
資料庫重建?

如果真的有需要新增欄位
可以直接在 Class 下新增
但要注意不要由遺漏的
沿用上一篇創立的表格 : Note
當創立完後
會產生以下兩個檔案
我們會修改此檔案











比如要在新增有關日期的欄位( 紅字代表新增 )

1 . Note.java ( 4項須更新 )
package de.greenrobot.daoexample;

// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. Enable "keep" sections if you want to edit. 
/**
 * Entity mapped to table NOTE.
 */
public class Note {

    private Long id;
    /** Not-null value. */
    private String note;
    private int count;
    /** Not-null value. */
    private java.util.Date date;
    /** Not-null value. */
    private java.util.Date Hee;

    public Note() {
    }

    public Note(Long id) {
        this.id = id;
    }

    public Note(Long id, String note, int count, java.util.Date date, java.util.Date Hee) {
        this.id = id;
        this.note = note;
        this.count = count;
        this.date = date;
        this.Hee = Hee;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    /** Not-null value. */
    public String getNote() {
        return note;
    }

    /** Not-null value; ensure this value is available before it is saved to the database. */
    public void setNote(String note) {
        this.note = note;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    /** Not-null value. */
    public java.util.Date getDate() {
        return date;
    }

    /** Not-null value; ensure this value is available before it is saved to the database. */
    public void setDate(java.util.Date date) {
        this.date = date;
    }

    /** Not-null value. */
    public java.util.Date getHee() {
        return Hee;
    }

    /** Not-null value; ensure this value is available before it is saved to the database. */
    public void setHee(java.util.Date Hee) {
        this.Hee = Hee;
    }

}

2 . NoteDao.java( 5項需更新 )

package de.greenrobot.daoexample;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;

import de.greenrobot.dao.AbstractDao;
import de.greenrobot.dao.Property;
import de.greenrobot.dao.internal.DaoConfig;

import de.greenrobot.daoexample.Note;

// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/** 
 * DAO for table NOTE.
*/
public class NoteDao extends AbstractDao<Note, Long> {

    public static final String TABLENAME = "NOTE";

    /**
     * Properties of entity Note.<br/>
     * Can be used for QueryBuilder and for referencing column names.
    */
    public static class Properties {
        public final static Property Id = new Property(0, Long.class, "id", true, "_id");
        public final static Property Note = new Property(1, String.class, "note", false, "NOTE");
        public final static Property Count = new Property(2, int.class, "count", false, "COUNT");
        public final static Property Date = new Property(3, java.util.Date.class, "date", false, "DATE");
        public final static Property Hee = new Property(4, java.util.Date.class, "Hee", false, "HEE");
    };


    public NoteDao(DaoConfig config) {
        super(config);
    }
    
    public NoteDao(DaoConfig config, DaoSession daoSession) {
        super(config, daoSession);
    }

    /** Creates the underlying database table. */
    public static void createTable(SQLiteDatabase db, boolean ifNotExists) {
        String constraint = ifNotExists? "IF NOT EXISTS ": "";
        db.execSQL("CREATE TABLE " + constraint + "'NOTE' (" + //
                "'_id' INTEGER PRIMARY KEY ," + // 0: id
                "'NOTE' TEXT NOT NULL ," + // 1: note
                "'COUNT' INTEGER NOT NULL ," + // 2: count
                "'DATE' INTEGER NOT NULL ," + // 3: date
                "'HEE' INTEGER NOT NULL );"); // 4: Hee
    }

    /** Drops the underlying database table. */
    public static void dropTable(SQLiteDatabase db, boolean ifExists) {
        String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "'NOTE'";
        db.execSQL(sql);
    }

    /** @inheritdoc */
    @Override
    protected void bindValues(SQLiteStatement stmt, Note entity) {
        stmt.clearBindings();
 
        Long id = entity.getId();
        if (id != null) {
            stmt.bindLong(1, id);
        }
        stmt.bindString(2, entity.getNote());
        stmt.bindLong(3, entity.getCount());
        stmt.bindLong(4, entity.getDate().getTime());
        stmt.bindLong(5, entity.getHee().getTime());
    }

    /** @inheritdoc */
    @Override
    public Long readKey(Cursor cursor, int offset) {
        return cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0);
    }    

    /** @inheritdoc */
    @Override
    public Note readEntity(Cursor cursor, int offset) {
        Note entity = new Note( //
            cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id
            cursor.getString(offset + 1), // note
            cursor.getInt(offset + 2), // count
            new java.util.Date(cursor.getLong(offset + 3)), // date
            new java.util.Date(cursor.getLong(offset + 4)) // Hee
        );
        return entity;
    }
     
    /** @inheritdoc */
    @Override
    public void readEntity(Cursor cursor, Note entity, int offset) {
        entity.setId(cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0));
        entity.setNote(cursor.getString(offset + 1));
        entity.setCount(cursor.getInt(offset + 2));
        entity.setDate(new java.util.Date(cursor.getLong(offset + 3)));
        entity.setHee(new java.util.Date(cursor.getLong(offset + 4)));
     }
    
    /** @inheritdoc */
    @Override
    protected Long updateKeyAfterInsert(Note entity, long rowId) {
        entity.setId(rowId);
        return rowId;
    }
    
    /** @inheritdoc */
    @Override
    public Long getKey(Note entity) {
        if(entity != null) {
            return entity.getId();
        } else {
            return null;
        }
    }

    /** @inheritdoc */
    @Override    
    protected boolean isEntityUpdateable() {
        return true;
    }
    
}

歡迎轉載,請註明出處。

2015年10月30日 星期五

Android-曲線圖 折線圖

只要有關數據分析的 App 我相信一定對曲線圖不陌生吧
今天就來做一個簡易的 Sample ( 範例 )

首先分析順序 :

  1. 資料
  2. 曲線圖 折線圖 風格
  3. 放置目標


activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/LinearLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:text="@string/str_tv_title" />

    <LinearLayout
        android:id="@+id/chart_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:layout_margin="8dip" >
    </LinearLayout>

</LinearLayout>


MainActivity.class
package com.example.ach;

import java.util.ArrayList;
import java.util.List;

import org.achartengine.ChartFactory;
import org.achartengine.chart.PointStyle;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.model.XYSeries;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;

public class MainActivity extends Activity {
    
    private View mChart;
     
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        
        List<String> list_D = new ArrayList<String>();
        for(int i = 0; i <10; i++){
            list_D.add("" + 100+i);
        }
        
        List<Integer> list_C = new ArrayList<Integer>();
        for(int i = 0; i <10; i++){
            list_C.add((int)(Math.random()*42+1));
        }
        
        LinearLayout chartContainer = (LinearLayout) findViewById(R.id.chart_container);
        chartContainer.addView(LineChartView.ChartView(MainActivity.this, "Hello", "Count", list_D, list_C));
                
    }    

}


LineCharView.class
package com.example.ach;

import java.util.List;

import org.achartengine.ChartFactory;
import org.achartengine.chart.PointStyle;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.model.XYSeries;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;

import android.content.Context;
import android.graphics.Color;
import android.view.View;

public class LineChartView {

    private static Context mContext;
    /**
     * 
     * @param chartTitle GraphTitle
     * @param XYSeries meaning of Series
     * @param list_Date X Data
     * @param list_Count Y Data
     * @return
     */
    public static View ChartView(Context context, String chartTitle, 
                String XYSeries, List<String> list_Date, List<Integer> list_Count) {
                    
        LineChartView.mContext = context;
        
        // 資料來源及命名
        XYSeries xySeries = new XYSeries(XYSeries);
        for(int i=0;i<list_Count.size();i++){
            xySeries.add(i+1, list_Count.get(i));
        }

        // Creating a dataset to hold each series
        XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();
        // Adding Income Series to the dataset
        dataset.addSeries(xySeries);

        // 線的描述
        XYSeriesRenderer xySeriesRenderer = new XYSeriesRenderer();
        xySeriesRenderer.setColor(Color.RED);
        xySeriesRenderer.setChartValuesTextSize(40);// Value Text Size
        xySeriesRenderer.setPointStyle(PointStyle.CIRCLE);
        xySeriesRenderer.setFillPoints(true);
        xySeriesRenderer.setLineWidth(5);
        xySeriesRenderer.setDisplayChartValues(true);

        XYMultipleSeriesRenderer multiRenderer = new XYMultipleSeriesRenderer();
        multiRenderer.setXLabels(0);
        multiRenderer.setChartTitle(chartTitle);
        multiRenderer.setChartTitleTextSize(40);
        multiRenderer.setXTitle("Date");// X Title
        multiRenderer.setYTitle("Count");// Y Title
        multiRenderer.setLabelsTextSize(40);// Label Text Size
        multiRenderer.setAxisTitleTextSize(40);// Axis Title Text Size
        multiRenderer.setZoomButtonsVisible(true);// Zoom?
        multiRenderer.setShowGrid(true);// show Grid
        for(int i=0; i<list_Date.size(); i++){
            multiRenderer.addXTextLabel(i+1, "" + list_Date.get(i).toString());
        }

        multiRenderer.addSeriesRenderer(xySeriesRenderer);

        View mChart = ChartFactory.getLineChartView(mContext, dataset, multiRenderer);

        return mChart;

    }

}


範例圖示 :























參考資料:MPAndroidChart
參考資料:MPAndroidChart 教程:概述
參考資料 : Android自定义控件 -Canvas绘制折线图(实现动态报表效果)
參考資料 : Android chart tutorial: AChartEngine - Line chart, Bar chart, Range bar
參考資料 : [Andriod] 透過AChartEngine實現BarChart
參考資料 : achartengine之折线图---简单用法

歡迎轉載,請註明出處。

2015年10月21日 星期三

ZigBee-簡介 說明

想投入IOT這塊一定對 : Zigbee 一定不陌生吧!
Zigbee 到底是什麼?

可以吃嗎?
生活中有嗎?
一些阿里不達的問題
.
.
.
.
之前我也思考很久
只知道一大堆的協定和一大堆看不懂的資料
但我只想簡單知道!!!

現在我來簡單的做整理
也會用簡單的圖示來呈現
希望各位能很快的漸入佳境

ZigBee : 一種傳輸協定( 例如 : 藍芽 )
傳輸距離 : 約 50 M
傳輸速率 : 250 kps
耗電 : 5 mA
好處 : 組網
型態 : coordinator 、router、end device
結構 : 樹狀

圖前解說 : 通常 Coordinator 用來接收所有裝置的封包( 有點像 : 頭目角色 )
                   所以一個族群裡只有一個頭目!

Coordinator 可串 Router 或 End Device
















Router 可串 : Router 或 End Device
End Device 不可串接















以下圖示是網路上常看到的
但現在改為顏色,好讓大家比較容易理解












如果看懂以上的圖
我想資訊網所提供的能更快速的理解。

Q&A 1 : 為什麼不 Router 和 End Device 全都接 Coordinator 就好 ?
A : 當然是可以的,但這不是 ZigBee 所樂見的。最終是要佈網才是他的優勢。

Q&A 2 : 如果以現實裝置來說,哪個是 Router? 哪個是 End Device?
A : 這個是好問題,這是由廠商來定義的。但這裡有分辨的小技巧,但還是
以原廠為主,看耗不耗電,如果需要用到插座通常是 Router ; 用電池供電通常
是 End Device 居多。

以上是個人的認知,如有錯誤
本人歡迎接受指教,謝謝大家

參考資料 : WiKi_ZigBee

歡迎轉載,請註明出處。

2015年10月19日 星期一

Android-Appium_腳本_執行順序

寫腳本的過程中
多少一定會有執行的順序
比如 : 
1_輸入帳號
2_輸入密碼
3_登入點擊
.
.
.
但是要怎麼寫程式才會乖乖地依序執行
@BeforeTest
@BeforeClass
@BeforeMethod
@testRegisterGateway
@AfterMethod
@AfterMethod
@AfterClass
@AfterTest

尚未完成

參考資料 : order of execution of tests in testng

2015年10月13日 星期二

Android-360混淆 Application

360 固仕 : 360加固保

各位大大如果完成自己 App 後
如果想上架
但又擔心自己的心血有心人反組譯
雖然混淆的方法有很多
但是 Dexguard 可以做混淆的動作
如果有第三方的 Jar 在編譯過程容易出錯
今天介紹 360
恩,不錯。

就是 360
這不是打廣告!
這不是打廣告!
這不是打廣告!


先提醒,此方法必須先上傳自己 APK 到 360 官網
等他編譯完再下載
如果真的有個人需求
這可能不符合你
所以真的想好後再去服用!!!

1 . 註冊帳號並且登入


 2 . 紅框框
下載應用程式
上傳開發者Apk
下載混淆後Apk


















3 . 開啟下載的檔案
注意 : 如有亂碼、空格請更改檔名,不然無法執行檔案。






4 . 將第 2 步處理好的Apk放置相關位置並且輸入 Keystone 相關資訊

















完成後就可以安心上架囉。



歡迎轉載,請註明出處。

2015年10月8日 星期四

2015年10月1日 星期四

Android-Appium 自動化測試 ( 三 )_Appium(舞台

當我們把腳本的執行檔案寫完後
舞台的特效就要開始做一些小小的設定















1 . 取得裝置序號
注意 : 必須是要打開開發者模式
命令提示字元打

adb kill-servier

adb devices


點選右鍵標註( 把序號反白複製 )

2 . 開啟 Appium 點擊 Android 圖示
將要測試的 APK 路徑寫入

選擇要測試的裝置 ( 二選一 )

* : 模擬器測試






















將Device Name : MyPhone 輸入在第二框框內

* : 裝置測試


現在將剛剛複製的裝置序號寫在第 2 框框內

3 . 點選設定按鈕
並且將紅色框框內的參數完整的輸入在框框內






















4 . 點選右上角執行



















等到以下程式 :

> info: Console LogLevel: debug

就可以等待連接了。
接下來我們可以做什麼 ?

1 . 藉由 Inspector Window 來查看元件屬性
2 . 執行我們所寫的腳本


之後全部的流程會在總結。

參考資料 : [Android] Appium 0.18.0 for Windows 新增的 Inspector 如何配置运行?
參考資料 : Android Automation using Appium in Windows
歡迎轉載,請註明出處。

2015年9月30日 星期三

Android-Appium 自動化測試 ( 一 )_說明

專案或者應用程式 告一個段落
我進行一連串的測試
但是光測試流程就得要花上半天的時間
然後又修改 Bug
然後又跑流程測試
然後就無限迴圈
這不知道是工程師的宿命還是個人的命運( 嘆

現在有 Appium 就可以幫我們做測試的動作了
這款工具可以支援 : ios 、 Android
今天就簡單介紹這款自動化測試工具
此篇解說 : Android 部分

1 . Appium 簡介 : 
( 有空補 )
----------------------------------------------------------------------
2 . Appium 架構( 這張圖只是簡化詳情請查Google ) :












先不要管 Appium,先想像如果要拍一部電影需要的是 ?
1 . 陽光 ?
2 . 空氣 ?
3 . 水 ?

都錯 !!!
1 . 待命好的演員( App . apk )
2 . 完美的劇本( TestCode )
3 . 多功能的場景( Appium )

有了這些,我想信你也能演出好的電影。
----------------------------------------------------------------------
3 . Appium 工具 :
( 有空補 )

4 . Appium 用法 :
( 有空補 )

5 . Appium 限制 :
Android SDK API >= 17

參考資訊 : Android UI Testing with Appium

歡迎轉載,請註明出處。

2015年9月29日 星期二

Python-基本運算( 四 )_List

上一篇提到 : 邏輯的判斷

這篇我們要提到的是 : List
在程式裡頭 List 是不可或缺的儲存空間
首先,我們先開啟 Python 環境
宣告方法

list = [123, 45, 841, 112, "asd", 'ase']

就可以用以下的方法來測試囉~
這裡就先列出最常用的幾個方法 :

新增list.append( item)
插入參數list.insert( index, item )
刪除( 參數 )list.remove( item )
刪除( 位置 )list.pop( index )
參數位置list.index( index )
排序( 小到大 )list.sort()
排序( 大到小 )list.reverse()
參數存在個數list.count( value )
陣列大小lenI( list )

* : item ( 參數( 字串 / 數字 皆可 ) )、index( 索引 )
* : Python 是直譯式語言


以下是執行畫面 :


















參考資料 : python Doc
歡迎轉載,請註明出處。