顯示具有 example 標籤的文章。 顯示所有文章
顯示具有 example 標籤的文章。 顯示所有文章

2015年3月13日 星期五

[iOS] UDP Socket 實作-含程式碼 / 完整範例 / Youtube 實測

終於可以把udp socket 做出來了!

每次google 都是那幾篇.....而且每個都寫很不完整(很多都抄同一篇的)

搞了半天什麼CocoaAsyncSocket , GCDAsyncSocket , BSD Socket.....

沒有一篇寫比較完整,後來還好在github上有一個可以用的(不過是for Mac ,not for

iphone) 於是就仿照著寫了以後就可以用了!

--------------------------------------------------------------------------------------------------------------------------

(正文開始)

要寫socket 一定是要分兩部分 server & client , 然後在眾多的library裡面我選擇是:

AsyncUdpSocket

首先,先開一個project,然後add files to (project name)...

把AsyncUdpSocket.h / AsyncUdpSocket.m 加入(放哪邊不重要,不影響)




然後我們需要先到ViewController.h / Main.storyboard來宣告/拉一些物件


這邊(server)會需要一個輸入port 的textfield,一個按鈕 以及一個textview

別忘記要把他們兩邊做連結!


ViewControlloer.h:

//
//  ViewController.h
//  UdpServer
//
//  Created by daniel on 2015/3/13.
//  Copyright (c) 2015 daniel. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "AsyncUdpSocket.h"

@interface ViewController : UIViewController
{
    AsyncUdpSocket *udpSocket;

}

@property (weak, nonatomic) IBOutlet UITextField *port;
@property (weak, nonatomic) IBOutlet UIButton *start;
@property (weak, nonatomic) IBOutlet UITextView *textview;

- (IBAction)startButton:(id)sender;


@end


接下來就是

ViewController.m:

//
//  ViewController.m
//  UdpServer
//
//  Created by daniel on 2015/3/13.
//  Copyright (c) 2015 daniel. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

@synthesize port;
@synthesize start;
@synthesize textview;

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    udpSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];//初始化
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)startButton:(id)sender {
        //start udp server
        NSLog(@"Socket open");
        int port_int = [port.text intValue];
        if(port_int < 0 || port_int > 65535)
        {
            port_int = 0;
        }
        
        NSError *error = nil;
        
        if (![udpSocket bindToPort:port_int error:&error]) {
            return;
        }
        
        [udpSocket receiveWithTimeout:-1 tag:0];
        [port setEnabled:NO];
}

-(BOOL) onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port
{
    NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    if(msg)
    {
        //textview.text  msg;
        NSLog(@"message is %@",msg);
        
        //以下這串是為了讓他能夠顯示在畫面上
        NSString *temp = [NSString stringWithFormat:@"%@\n",msg];
        NSMutableDictionary *attribute = [NSMutableDictionary dictionaryWithCapacity:1];
        NSAttributedString *as = [[NSAttributedString alloc] initWithString:temp attributes:attribute];
        //使用append的方式 ,但是他是NSAttributedString格式 所以才要先轉換,不能直接使用NSString!
        [[textview textStorage] appendAttributedString:as];
    }
    else
    {
        NSLog(@"converting UTF-8 Error");
    }
    [udpSocket sendData:data toHost:host port:port withTimeout:-1 tag:0];
    [udpSocket receiveWithTimeout:-1 tag:0];
    return YES;
}
@end


在viewDidLoad這邊就是app一開始會跑的地方,當然是把socket初始化

然後在button按下去觸發的這個函式(IBAction)startButton:(id)sender )

我們需要把使用者輸入的port 放進int port_int裡面

然後如果使用者輸入數字超過0~65535這個區間 就直接給他port = 0

接著在-(BOOL) onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port

這一段,我們必須要先宣告一個NSData來接收資料(data),編碼這邊使用UTF-8

如果msg有東西則顯示在螢幕上

記得最後面要加

    [udpSocket sendData:data toHost:host port:port withTimeout:-1 tag:0];

    [udpSocket receiveWithTimeout:-1 tag:0];

不然只能接收一次 !



--------------------------------------------------------------------------------------------------------------------------

然後就是 Client端了


一樣是把兩邊做連結(別忘了要import "AsyncUdpSocket.h")


//
//  ViewController.m
//  UdpClient
//
//  Created by daniel on 2015/3/13.
//  Copyright (c) 2015 daniel. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

@synthesize addr;
@synthesize port;
@synthesize msg;
@synthesize sendButton;
@synthesize logView;

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    udpSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
    NSError *error = nil;
    
    if(![udpSocket bindToPort:0 error:&error])
    {
        NSLog(@"Error binding: %@",error);
        return;
    }
    
    [udpSocket receiveWithTimeout:-1 tag:0];
    
    NSLog(@"Ready");
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


-(IBAction)send:(id)sender
{
    NSString *host = [addr text];
    NSLog(@"Address: %@",host);
    
    
    int port_int = [[port text] integerValue];//跟原本的不太一樣
    if(port_int <=0 || port > 65535)
    {
        NSLog(@"Valid port require");
    }
    
    NSString *message = [msg text];
    if (message.length == 0) {
        NSLog(@"Message require");
    }
    
    NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding];
    
    if(data != nil)
        NSLog(@"data = %@",data);
    
    [udpSocket sendData:data toHost:host port:5000 withTimeout:-1 tag:tag];
}


-(void) onUdpSocket:(AsyncUdpSocket *)sock didSendDataWithTag:(long)tag
{
    //you could add checks here
}

- (void)onUdpSocket:(AsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error
{
    // You could add checks here
}

-(BOOL) onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port
{
    
    NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    if(msg)
    {
        NSLog(@"Receive: %@",msg);
    }
    else
    {
        NSLog(@"Receive Unknown message from %@:%hu",host,port);
    }
    [udpSocket receiveWithTimeout:-1 tag:0];
    return YES;
}

@end

在viewDidLoad這邊一樣要先初始化udpSocket,然後在sendbutton觸發這個action的

event這邊:
-(IBAction)send:(id)sender
{
    NSString *host = [addr text];
    把使用者輸入的ip放入host變數   

    NSLog(@"Address: %@",host);
    
    
    int port_int = [port text];
    
    把使用者輸入的port放進去port_int變數   

    if(port_int <=0 || port > 65535)
    {
        NSLog(@"Valid port require");
    }
    
    NSString *message = [msg text];

    把使用者輸入的message放進去message變數

    if (message.length == 0) {
        NSLog(@"Message require");
    }
    
    NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding];
    
   把要傳出去的message轉換為NSData型態,編碼使用utf-8

    if(data != nil)
        NSLog(@"data = %@",data);
    
    [udpSocket sendData:data toHost:host port:5000 withTimeout:-1 tag:tag];
    
    這邊為了測試方便直接把port 寫死5000,其實應該要寫port:port_int才對
}


好了!

這樣server / client 都有了 就可以開始通訊拉!




附上程式碼!


https://www.youtube.com/watch?v=wNX29RxA2K8&feature=youtu.be
https://www.youtube.com/watch?v=mDJv2UHnyZg&feature=youtu.be
附上實測證明可以用

2015年2月2日 星期一

[Android] ViewPager的簡易使用方法,Fragment,以及切換分頁監聽實作

之前看一些可以分頁的APP又有動畫,都只是把程式碼抓下來修改一下,就用了.

今天終於稍微搞懂一些裡面的用法.

首先MainActivity.java

1.第一目標 ------ extends FragmentActivity

然後裡面宣告一個PagerAdapter 的變數

2.PagerAdapter宣告的時候一定是出現錯誤,因為你還沒建立一個名為PagerAdapter的型態的

class 所以我們就多建一個java檔名為PagerAdapter 記得要implement FragmentPagerAdapter!

import java.util.List;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

public class PagerAdapter extends FragmentPagerAdapter {


private List<Fragment> fragments;

public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
// TODO Auto-generated constructor stub
this.fragments = fragments;
}

@Override
public Fragment getItem(int arg0) {
// TODO Auto-generated method stub
return this.fragments.get(arg0);
}

@Override
public int getCount() {
// TODO Auto-generated method stub
return this.fragments.size();
}

}

裡面就是實作他需要的方法(可以按add unimplement method來建立)

3.把PagerAdapter建立起來以後就是要加入分頁了!

在onCreate()裡面新增:

(初始化pager)
private void initialisePaging() {
// TODO Auto-generated method stub
List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, Fragment1.class.getName()));
fragments.add(Fragment.instantiate(this, Fragment2.class.getName()));
mPagerAdapter = new PagerAdapter(this.getSupportFragmentManager(),
fragments);

ViewPager pager = (ViewPager) findViewById(R.id.viewpager);
pager.setAdapter(mPagerAdapter);

myPageChange = new myPageChangeListenter();//new
pager.setOnPageChangeListener(myPageChange);//建立監聽
}

這邊就是建立一個list 然後放進去兩個class(也就是未來的兩個分頁)

當然要在外面宣告兩個變數

private PagerAdapter mPagerAdapter;
public myPageChangeListenter myPageChange;//先宣告一個該型態的變數

不然他會抓不到

--------------------------------------------分隔線------------------------------------------------------

如果到這邊都OK的話,你已經完成一半了.

接下來還要設定監聽(滑動的時候要監聽,不然系統不會去抓你滑到哪一頁了)

我們在上面有用pager.setOnPageChangeListener(myPageChange);來建立監聽

下面當然是要實做這個 myPageChangeListener

public class myPageChangeListenter implements OnPageChangeListener{

@Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
//Log.d("change to page = ",Integer.toString(arg0));
}

@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
//Log.d("scroll to page = ","here");
}

@Override
public void onPageSelected(int arg0) {
// TODO Auto-generated method stub
Log.d("select to page = ",Integer.toString(arg0));
//第一頁為0,第二頁為1
}
}

}

非常簡單,我個人習慣使用onPageSelected來設定動作,都可以透過Log來測試你想要的觸發

--------------------------------------------分隔線------------------------------------------------------

有了 監聽 /  PagerAdapter  還需要分頁.....

這部分最大的問題就是在於抓不到該分頁的元件id,當然後來解決了(不然也不會發文)

package com.example.viewpagerexample;

import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.civetcat.weather.library.WeatherInfo;
import com.civetcat.weather.library.YahooWeather;
import com.civetcat.weather.library.YahooWeather.SEARCH_MODE;
import com.civetcat.weather.library.YahooWeatherInfoListener;

public class Fragment1 extends Fragment implements YahooWeatherInfoListener{

    public View view;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
if (container == null) {
return null;
}

view = inflater.inflate(R.layout.fragement1_layout,container, false);

               //中間可以使用
               TextView txv = (TextView) view.findViewById(R.id.xxx);
               //來抓取id

return view;
}
}

Fragment2 也是類似的就不多貼了!

至於layout的部分:

(viewpager_layout.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" >
   
    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />
</LinearLayout>

這部分是重點!!

看我們這邊在MainActivity.java裡面設定的

setContentView(R.layout.viewpager_layout);

因此該xml檔必須要有ViewPager 不然怎麼去抓呢? 

大致上就是這樣,把寫了一個天氣的範例放上來,可以參考看看


END


2014年9月1日 星期一

[Andorid] Service 範例-背景執行/無UI

要做一個Service要先了解,service無法自己啟動,必須要靠startService()才能啟動。

同樣的啟動後也必須要使用stopService()關閉。

在這邊需要注意的有幾點

1) startService()

2) stopService()

3) AndroidManiFest.xml增加service權限


第一點為開啟service必要條件,當然你要new一個intent

所以就是:

Intent intent = new Intent(MainActivity.this,service_class.class);
startService(intent);

MainActivity為你目前的class,service_class則是另外開的java檔(那邊則是寫service做的事情)


第二點跟第一點很類似,只有差在start / stop的差別

Intent intent = new Intent(MainActivity.this,service_class.class);
stopService(intent);

第三點就是你需要在Android ManiFest.xml裡面新增權限

否則跑起來會有

"

Unable to start service Intent: not found

"
這樣的錯誤訊息出現

好了,總結上面 MainActivity該如何寫?

package com.example.service_nonui_demo;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.os.Build;

public class MainActivity extends ActionBarActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(MainActivity.this,service_class.class);
startService(intent);
}
@Override
public void onPause(){
super.onPause();
Intent intent = new Intent(MainActivity.this,service_class.class);
stopService(intent);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}

/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {

public PlaceholderFragment() {
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
return rootView;
}
}

}

Service部分

package com.example.service_nonui_demo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;

public class service_class extends Service{

@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
}
 

@Override
public void onStart(Intent intent, int startId) {
Toast.makeText(this, "Service start", Toast.LENGTH_SHORT).show();
}
public void onDestroy(){
super.onDestroy();
Toast.makeText(this, "Service stop", Toast.LENGTH_SHORT).show();
}
}

(記得要extends Service!)

Android ManiFest.xml部分

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.service_nonui_demo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.service_nonui_demo.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
            <service android:name="service_class"></service>
    </application>

</manifest>

記得要新增紅色字的這一行

如此一來,service就可以啟動了!

service在onStart()的地方開始運作,所以如果要做任何動作,可以寫在裡面喔!

範例只是做一個toast(跳出訊息)而已。

附上範例檔案 **點這裡下載**

End