Day: January 8, 2021
sessão 10 – Programação gráfica orientada a eventos
Referências bibliográficas:
Capítulos 2-5 e 7-9 do Livro Windows NT 4 Programming
MSDN:
Introduction to Windows Programming in C++
https://msdn.microsoft.com/en-us/library/ff381398(v=vs.85).aspx
Mouse Input
https://msdn.microsoft.com/en-us/library/gg153549(v=vs.85).aspx
Keyboard Input
https://msdn.microsoft.com/en-us/library/gg153546(v=vs.85).aspx
Mouse Movement
https://msdn.microsoft.com/en-us/library/gg153550(v=vs.85).aspx
Dialog Boxes
https://msdn.microsoft.com/en-us/library/windows/desktop/ms632588(v=vs.85).aspx
Menus
https://msdn.microsoft.com/en-us/library/windows/desktop/ms646977(v=vs.85).aspx
Windows Graphics Device Interface (GDI)
https://msdn.microsoft.com/en-us/library/dd145203(v=vs.85).aspx
Tutorial online: Win32 Fundamentals
http://www.functionx.com/win32/Lesson01.htm
Events
http://www.functionx.com/win32/Lesson05.htm
Object-Oriented Win32
http://www.functionx.com/win32/Lesson06.htm
Como começar?
no visualstudio 2019, podemos começar com uma aplicação normal de consola.
alterar de projeto gráfico nas propriedades
um exemplo de um ficheiro base:
nclude <windows.h> #include <tchar.h> /* ===================================================== */ /* Programa base (esqueleto) para aplicações Windows */ /* ===================================================== */ // Cria uma janela de nome "Janela Principal" e pinta fundo de branco // Modelo para programas Windows: // Composto por 2 funções: // WinMain() = Ponto de entrada dos programas windows // 1) Define, cria e mostra a janela // 2) Loop de recepção de mensagens provenientes do Windows // TrataEventos()= Processamentos da janela (pode ter outro nome) // 1) É chamada pelo Windows (callback) // 2) Executa código em função da mensagem recebida LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); // Nome da classe da janela (para programas de uma só janela, normalmente este nome é // igual ao do próprio programa) "szprogName" é usado mais abaixo na definição das // propriedades do objecto janela TCHAR szProgName[] = TEXT("Base"); // ============================================================================ // FUNÇÃO DE INÍCIO DO PROGRAMA: WinMain() // ============================================================================ // Em Windows, o programa começa sempre a sua execução na função WinMain()que desempenha // o papel da função main() do C em modo consola WINAPI indica o "tipo da função" (WINAPI // para todas as declaradas nos headers do Windows e CALLBACK para as funções de // processamento da janela) // Parâmetros: // hInst: Gerado pelo Windows, é o handle (número) da instância deste programa // hPrevInst: Gerado pelo Windows, é sempre NULL para o NT (era usado no Windows 3.1) // lpCmdLine: Gerado pelo Windows, é um ponteiro para uma string terminada por 0 // destinada a conter parâmetros para o programa // nCmdShow: Parâmetro que especifica o modo de exibição da janela (usado em // ShowWindow() int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; // hWnd é o handler da janela, gerado mais abaixo por CreateWindow() MSG lpMsg; // MSG é uma estrutura definida no Windows para as mensagens WNDCLASSEX wcApp; // WNDCLASSEX é uma estrutura cujos membros servem para // definir as características da classe da janela // ============================================================================ // 1. Definição das características da janela "wcApp" // (Valores dos elementos da estrutura "wcApp" do tipo WNDCLASSEX) // ============================================================================ wcApp.cbSize = sizeof(WNDCLASSEX); // Tamanho da estrutura WNDCLASSEX wcApp.hInstance = hInst; // Instância da janela actualmente exibida // ("hInst" é parâmetro de WinMain e vem // inicializada daí) wcApp.lpszClassName = szProgName; // Nome da janela (neste caso = nome do programa) wcApp.lpfnWndProc = TrataEventos; // Endereço da função de processamento da janela // ("TrataEventos" foi declarada no início e // encontra-se mais abaixo) wcApp.style = CS_HREDRAW | CS_VREDRAW; // Estilo da janela: Fazer o redraw se for // modificada horizontal ou verticalmente wcApp.hIcon = LoadIcon(NULL, IDI_APPLICATION); // "hIcon" = handler do ícon normal // "NULL" = Icon definido no Windows // "IDI_AP..." Ícone "aplicação" wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); // "hIconSm" = handler do ícon pequeno // "NULL" = Icon definido no Windows // "IDI_INF..." Ícon de informação wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); // "hCursor" = handler do cursor (rato) // "NULL" = Forma definida no Windows // "IDC_ARROW" Aspecto "seta" wcApp.lpszMenuName = NULL; // Classe do menu que a janela pode ter // (NULL = não tem menu) wcApp.cbClsExtra = 0; // Livre, para uso particular wcApp.cbWndExtra = 0; // Livre, para uso particular wcApp.hbrBackground =(HBRUSH)GetStockObject(WHITE_BRUSH); // "hbrBackground" = handler para "brush" de pintura do fundo da janela. Devolvido por // "GetStockObject".Neste caso o fundo será branco // ============================================================================ // 2. Registar a classe "wcApp" no Windows // ============================================================================ if (!RegisterClassEx(&wcApp)) return(0); // ============================================================================ // 3. Criar a janela // ============================================================================ hWnd = CreateWindow( szProgName, // Nome da janela (programa) definido acima TEXT("Exemplo de Janela Principal em C"),// Texto que figura na barra do título WS_OVERLAPPEDWINDOW, // Estilo da janela (WS_OVERLAPPED= normal) CW_USEDEFAULT, // Posição x pixels (default=à direita da última) CW_USEDEFAULT, // Posição y pixels (default=abaixo da última) CW_USEDEFAULT, // Largura da janela (em pixels) CW_USEDEFAULT, // Altura da janela (em pixels) (HWND) HWND_DESKTOP, // handle da janela pai (se se criar uma a partir de // outra) ou HWND_DESKTOP se a janela for a primeira, // criada a partir do "desktop" (HMENU) NULL, // handle do menu da janela (se tiver menu) (HINSTANCE) hInst, // handle da instância do programa actual ("hInst" é // passado num dos parâmetros de WinMain() 0); // Não há parâmetros adicionais para a janela // ============================================================================ // 4. Mostrar a janela // ============================================================================ ShowWindow(hWnd, nCmdShow); // "hWnd"= handler da janela, devolvido por // "CreateWindow"; "nCmdShow"= modo de exibição (p.e. // normal/modal); é passado como parâmetro de WinMain() UpdateWindow(hWnd); // Refrescar a janela (Windows envia à janela uma // mensagem para pintar, mostrar dados, (refrescar)… // ============================================================================ // 5. Loop de Mensagens // ============================================================================ // O Windows envia mensagens às janelas (programas). Estas mensagens ficam numa fila de // espera até que GetMessage(...) possa ler "a mensagem seguinte" // Parâmetros de "getMessage": // 1)"&lpMsg"=Endereço de uma estrutura do tipo MSG ("MSG lpMsg" ja foi declarada no // início de WinMain()): // HWND hwnd handler da janela a que se destina a mensagem // UINT message Identificador da mensagem // WPARAM wParam Parâmetro, p.e. código da tecla premida // LPARAM lParam Parâmetro, p.e. se ALT também estava premida // DWORD time Hora a que a mensagem foi enviada pelo Windows // POINT pt Localização do mouse (x, y) // 2)handle da window para a qual se pretendem receber mensagens (=NULL se se pretendem // receber as mensagens para todas as // janelas pertencentes à thread actual) // 3)Código limite inferior das mensagens que se pretendem receber // 4)Código limite superior das mensagens que se pretendem receber // NOTA: GetMessage() devolve 0 quando for recebida a mensagem de fecho da janela, // terminando então o loop de recepção de mensagens, e o programa while (GetMessage(&lpMsg,NULL,0,0)) { TranslateMessage(&lpMsg); // Pré-processamento da mensagem (p.e. obter código // ASCII da tecla premida) DispatchMessage(&lpMsg); // Enviar a mensagem traduzida de volta ao Windows, que // aguarda até que a possa reenviar à função de // tratamento da janela, CALLBACK TrataEventos (abaixo) } // ============================================================================ // 6. Fim do programa // ============================================================================ return((int)lpMsg.wParam); // Retorna sempre o parâmetro wParam da estrutura lpMsg } // ============================================================================ // FUNÇÃO DE PROCESSAMENTO DA JANELA // Esta função pode ter um nome qualquer: Apenas é necesário que na inicialização da // estrutura "wcApp", feita no início de // WinMain(), se identifique essa função. Neste // caso "wcApp.lpfnWndProc = WndProc" // // WndProc recebe as mensagens enviadas pelo Windows (depois de lidas e pré-processadas // no loop "while" da função WinMain() // Parâmetros: // hWnd O handler da janela, obtido no CreateWindow() // messg Ponteiro para a estrutura mensagem (ver estrutura em 5. Loop... // wParam O parâmetro wParam da estrutura messg (a mensagem) // lParam O parâmetro lParam desta mesma estrutura // // NOTA:Estes parâmetros estão aqui acessíveis o que simplifica o acesso aos seus valores // // A função EndProc é sempre do tipo "switch..." com "cases" que descriminam a mensagem // recebida e a tratar. // Estas mensagens são identificadas por constantes (p.e. // WM_DESTROY, WM_CHAR, WM_KEYDOWN, WM_PAINT...) definidas em windows.h // ============================================================================ LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { switch (messg) { case WM_DESTROY: // Destruir a janela e terminar o programa // "PostQuitMessage(Exit Status)" PostQuitMessage(0); break; default: // Neste exemplo, para qualquer outra mensagem (p.e. "minimizar","maximizar","restaurar") // não é efectuado nenhum processamento, apenas se segue o "default" do Windows return(DefWindowProc(hWnd,messg,wParam,lParam)); break; // break tecnicamente desnecessário por causa do return } return(0); }
uma unica janela e com a informação persistente..
#include <windows.h> #include <tchar.h> #include <windowsx.h> LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); //main int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; //caracteristicas especificas de todas as janelas, comuns: // o ícone, título, cor de fundo, localização wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_WARNING); wcApp.hIconSm = LoadIcon(NULL, IDI_SHIELD); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW ); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; //wcApp.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); if (!RegisterClassEx(&wcApp)) return(0); //caracteristicas especificas de cada Janela!!! -> CreateWindow hWnd = CreateWindow( szProgName, TEXT("S02 - Exercício 01"), WS_OVERLAPPEDWINDOW, 200, // Posição x pixels 200, // Posição y pixels 600, // Largura da janela (em pixels) 600, // Altura da janela (em pixels) (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); //ciclo que mantem o programa a funcionar while (GetMessage(&lpMsg, NULL,0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } //função que trata os eventos, chamada pelo sistema operativo //esta função pode estar associada a diferentes janelas //coleção de eventos: UINT messg //TCHAR c = '?'; //global //para guardar o historial dos desenhos typedef struct { int xPos; int yPos; TCHAR c; } PosChar; LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { HDC hdc; RECT rect; //int xPos, yPos; //static int xPos, yPos; //serem globais ou static para serem presistentes static PosChar posicoes[500]; //array de posições static int totalPos = 0; int i; static TCHAR chardAtual = '?'; //ou local PAINTSTRUCT ps; switch (messg) { //evento que é sempre disparado qd acontece o refresh! //todas as operações que escrevem na janela têm que surgir aqui WM_PAINT //todas as operaçõe de escrita é aqui WM_PAINT case WM_PAINT: hdc = BeginPaint(hWnd, &ps); //inicio de escrita na janela //deseho //hdc = GetDC(hWnd); GetClientRect(hWnd, &rect); //toda a area do cliente FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); //fundo da janela SetTextColor(hdc, RGB(255, 0, 255)); SetBkMode(hdc, TRANSPARENT); for (i = 0; i < totalPos; i++) { rect.left = posicoes[i].xPos; rect.top = posicoes[i].yPos; DrawText(hdc, &posicoes[i].c, 1, &rect, DT_SINGLELINE | DT_NOCLIP); } //ReleaseDC(hWnd, hdc); EndPaint(hWnd, &ps); //fim de escrita na janela break; //ignorar a limpeza do fundo, depois é usado o FillRect no WM_PAINT case WM_ERASEBKGND: return TRUE; /*case WM_PAINT: for(auto i : ops) { hdc = GetDC(hWnd); GetClientRect(hWnd, &rect); rect.bottom = i.xPos; rect.top = i.yPos; SetTextColor(hdc, RGB(0, 0, 0)); SetBkMode(hdc, TRANSPARENT); DrawText(hdc, &i.pChar, 1, &rect, DT_SINGLELINE | DT_NOCLIP); ReleaseDC(hWnd, hdc); } break; */ case WM_LBUTTONDOWN: //xPos = GET_X_LPARAM(lParam); //#include <windowsx.h> //yPos = GET_Y_LPARAM(lParam); //#include <windowsx.h> if(totalPos<500){ posicoes[totalPos].xPos = GET_X_LPARAM(lParam); //#include <windowsx.h> posicoes[totalPos].yPos = GET_Y_LPARAM(lParam); //#include <windowsx.h> posicoes[totalPos].c = chardAtual; totalPos++; InvalidateRect(hWnd, NULL, FALSE); //refresh da janela, ajuda a lidar com o fundo } ////deseho //hdc = GetDC(hWnd); //GetClientRect(hWnd, &rect); //SetTextColor(hdc, RGB(255, 0, 255)); //SetBkMode(hdc, TRANSPARENT); //rect.left = xPos; //rect.top = yPos; //DrawText(hdc, &c, 1, &rect, DT_SINGLELINE | DT_NOCLIP); //ReleaseDC(hWnd, hdc); break; case WM_CHAR: chardAtual = (TCHAR)wParam; break; case WM_CLOSE: if( MessageBox(hWnd, TEXT("Tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
lidar com animações e com o flickering / oscilante
#include <windows.h> #include <tchar.h> #include <windowsx.h> LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); //(todas elas vão ser inicializadas na main, e não podem ser usadas variaveis estáticas) //mas podia estar dentro de uma estrutura //handle/acesso para o bitmap HBITMAP hBmp; //handle para o device content, o que vai permitir passar o bmp para este formato HDC bmpDC; //informação sobre a estrutura do bitmap (pe: largura e altura..) BITMAP bmp; //posição da imagem int xBitmap; int yBitmap; //ou isto assim agora ou criar uma estrutura! int limDireitoJanela; HWND hWandGlobal; //lidar com o flikring a centilação e lidar com os acesso concorrentes: mutex HANDLE hMutex; //lidar com a copia em memoria, double buffering HDC memoriaCopiaDC = NULL; HBITMAP hCopiaBitMap;//vai copiar as caracteristicas para a copia //crair uma thread para lidar com as animações/tempo que passa DWORD WINAPI movimentaImagem(LPVOID param) { int direcaoMovimento = 1; //1, direita e -1, esquerda int salto = 2; //salto de posições em termos de pixeis while(1) { WaitForSingleObject(hMutex, INFINITE); xBitmap = xBitmap + (direcaoMovimento * salto); if(xBitmap <=0) { xBitmap = 0; direcaoMovimento = 1; } else if(xBitmap >= limDireitoJanela) { xBitmap = limDireitoJanela; direcaoMovimento = -1; } ReleaseMutex(hMutex); //temos sempre que avisar o sistema para fazer uma nova pintura da janela InvalidateRect(hWandGlobal, NULL, FALSE); Sleep(1); } } int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; //caracteristicas especificas de todas as janelas, comuns: // o ícone, título, cor de fundo, localização wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_WARNING); wcApp.hIconSm = LoadIcon(NULL, IDI_SHIELD); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; //wcApp.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); if (!RegisterClassEx(&wcApp)) return(0); //caracteristicas especificas de cada Janela!!! -> CreateWindow hWnd = CreateWindow( szProgName, TEXT("S02 - Exercício 01"), WS_OVERLAPPEDWINDOW, 200, // Posição x pixels 200, // Posição y pixels 600, // Largura da janela (em pixels) 600, // Altura da janela (em pixels) (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); //aqui vem a a informação do bitmap //1º carregar o bitmap (124x124) hBmp = LoadImage(NULL, TEXT("flag.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //ir buscar informações sobre o recurso, carregar os metadados da imagem GetObject(hBmp, sizeof(bmp), &bmp); //passar do handle da imagem para o device content HDC hdc; hdc = GetDC(hWnd); //criar uma copia bmpDC = CreateCompatibleDC(hdc); //devolve um novo handle //aplicar o bitmap ao device content SelectObject(bmpDC, hBmp); //fechar o device content da janela ReleaseDC(hWnd, hdc); //definir as posições iniciais da imagem, e centrar por exemplo RECT rect; GetClientRect(hWnd, &rect); //xBitmap = (LARGURA_JANELA / 2) - (bmp.bmWidth/2); //yBitmap = (ALTURA_JANELA / 2) - (bmp.bmHeight / 2); xBitmap = (rect.right / 2) - (bmp.bmWidth/2); yBitmap = (rect.bottom / 2) - (bmp.bmHeight / 2); //escrever a imagem no tratamento de eventos.. //definir o que é o limite dieito limDireitoJanela = rect.right - bmp.bmWidth; hWandGlobal = hWnd; hMutex = CreateMutex(NULL, FALSE, NULL); //criar a thread CreateThread(NULL, 0, movimentaImagem, NULL, 0, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); //ciclo que mantem o programa a funcionar while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { HDC hdc; RECT rect; PAINTSTRUCT ps; switch (messg) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); //inicio de escrita na janela GetClientRect(hWnd, &rect); //toda a area do cliente //fazer a copia para o buffering if(memoriaCopiaDC == NULL) { //ainda nao foi feita a copia em memoria //criar a copia memoriaCopiaDC = CreateCompatibleDC(hdc); //obter as caracteristicas, copia figdigna mas falta lidar com o resize.. hCopiaBitMap = CreateCompatibleBitmap(hdc, rect.right, rect.bottom); SelectObject(memoriaCopiaDC, hCopiaBitMap); DeleteObject(hCopiaBitMap); //libertar recursos } //FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); //fundo da janela FillRect(memoriaCopiaDC, &rect, CreateSolidBrush(RGB(0, 255, 0))); //fundo da janela WaitForSingleObject(hMutex, INFINITE); //BitBlt(hdc, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); BitBlt(memoriaCopiaDC, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); //SRCCOPY copiar a totalidade dos pixeis ReleaseMutex(hMutex); // resolver problemas do xBitmap, yBitmap //vai ser necessário copiar para a principal BitBlt(hdc,0,0, rect.right, rect.bottom, memoriaCopiaDC, 0, 0, SRCCOPY); EndPaint(hWnd, &ps); break; //lidar com o resize da janela, redimensionamento da janela case WM_SIZE: WaitForSingleObject(hMutex, INFINITE); //LOWORD(lParam) -> menos significativa representa a largura //HIWORD(lParam) -> mais significativa representa a altura xBitmap = (LOWORD(lParam) / 2) - (bmp.bmWidth / 2); yBitmap = (HIWORD(lParam) / 2) - (bmp.bmHeight / 2); //temos tb que ajusatr o limite da janela limDireitoJanela = LOWORD(lParam) - bmp.bmWidth; //libterar o memoriaCopiaDC ReleaseDC(hWnd, memoriaCopiaDC); memoriaCopiaDC = NULL; ReleaseMutex(hMutex); break; case WM_ERASEBKGND: return TRUE; case WM_CLOSE: if ( MessageBox(hWnd, TEXT("Tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
em kotlin: gps terceira versão com mapa
Adicionar uma dependência via menus no build-gradle (module)
(1)
ficheiro: build-gradle(module)
... dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.2.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'com.google.android.gms:play-services-location:17.1.0' implementation 'com.google.android.gms:play-services-maps:17.0.0' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' }
ficheiro: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pt.isec.estuda.amov_gpsv1"> <!-- pedir estas permissões --> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- pedir estas permissões para aceder aos mapas e ao estado da rede não são em runtime --> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Amov_gpsv1"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <meta-data android:name="com.google.android.geo.API_KEY" android:value="AIzaSyD4M076vcUKcPZNGlyBzFshU-J4jJ98x5g"/> </application> </manifest>
ficheiro: MainActivity.kt
package pt.isec.estuda.amov_gpsv1 import android.Manifest import android.annotation.SuppressLint import android.content.pm.PackageManager import android.graphics.Color import android.location.Location import android.location.LocationListener import android.location.LocationManager import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import androidx.core.app.ActivityCompat import com.google.android.gms.location.* import com.google.android.gms.maps.CameraUpdateFactory import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.OnMapReadyCallback import com.google.android.gms.maps.SupportMapFragment import com.google.android.gms.maps.model.CameraPosition import com.google.android.gms.maps.model.CircleOptions import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.MarkerOptions const val TAG = "Location" class MainActivity : AppCompatActivity(), OnMapReadyCallback{ var locEnable = false //caso não ajam permissões lateinit var fLoc : FusedLocationProviderClient val ISEC = LatLng(40.1925, -8.4115) val DEIS = LatLng(40.1925, -8.4128) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) fLoc = FusedLocationProviderClient(this) //pedir o objeto google maps e vai ser necessário o OnMapReadyCallback (supportFragmentManager.findFragmentById(R.id.map) as? SupportMapFragment)?.getMapAsync(this) } override fun onResume() { super.onResume() startLocationServices(true) //para poupanças de energia, ativar o mais tarde possivel } override fun onPause() { super.onPause() //para poupanças de energia, desactivar o mais cedo possivel if(locEnable) { locEnable = false } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) //se tivermos as permissões //locationM.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 100f, this) if(requestCode == 1289) { startLocationServices(false) } } fun startLocationServices(askPerm : Boolean) { //1ºprovider, 2º intervalo de segundos,3º distancia minima ,4º o listener //se for for telemovel NETWORK_PROVIDER //dar permissões: ACCESS_FINE_LOCATION //e pedir em runtime, add permissions check if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { //pedir as permissões //&& ou || if (askPerm) { ActivityCompat.requestPermissions(this, arrayOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION), 1289) //1289 um valor qualquer! } else { finish() } return } val locRequest = LocationRequest().apply { interval = 4000 priority = LocationRequest.PRIORITY_HIGH_ACCURACY //ocultar para ja estes dois: //fastestInterval = 2000 //maxWaitTime = 10000 //numUpdates /? a ver, quantidade de updates } //activar locEnable = true fLoc.requestLocationUpdates(locRequest, locationCallBack, null) } //função locationCallBack var locationCallBack = object : LocationCallback() { override fun onLocationResult(p0: LocationResult?) { Log.i(TAG, "onLocationAvailability: ") //locations é uma lista de localizações p0?.locations?.forEach { Log.i(TAG, "onLocationAvailability: ${it.latitude} ${it.longitude}") } } } @SuppressLint("MissingPermission") override fun onMapReady(map: GoogleMap?) { //implementar o omMapReady, sendo p0 o ojecto google maps map ?: return //if map == null return map.isMyLocationEnabled = true map.uiSettings.isCompassEnabled = true map.uiSettings.isZoomControlsEnabled = true map.uiSettings.isZoomGesturesEnabled = true val cp = CameraPosition.Builder().target(ISEC).zoom(17f).bearing(0f).tilt(0f).build() map.animateCamera(CameraUpdateFactory.newCameraPosition(cp)) map.addCircle( CircleOptions().center(ISEC).radius(150.0).fillColor(Color.argb(128,128,128,128)).strokeColor(Color.rgb(128,0,0)).strokeWidth(4f) ) val mo = MarkerOptions().position(ISEC).title("ISEC-IPC").snippet("Instituo.....") val isec = map.addMarker(mo) isec.showInfoWindow() map.addMarker(MarkerOptions().position(DEIS).title("DEIS-ISEC")) } }
ficheiro: activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout 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" tools:context=".MainActivity"> <!-- incluimos o mapa como um fragmento --> <fragment android:id="@+id/map" android:layout_width="match_parent" android:layout_height="match_parent" android:name="com.google.android.gms.maps.SupportMapFragment"/> </FrameLayout>
em kotlin: gps segunda versão
Adicionar uma dependência via menus no build-gradle (module)
(1)
(2)
(3)
ficheiro: build-gradle(module)
... dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.2.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'com.google.android.gms:play-services-location:17.1.0' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' }
ficheiro: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pt.isec.estuda.amov_gpsv1"> <!-- pedir estas permissões --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Amov_gpsv1"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
ficheiro: MainActivity.kt
package pt.isec.estuda.amov_gpsv1 import android.Manifest import android.content.pm.PackageManager import android.location.Location import android.location.LocationListener import android.location.LocationManager import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import androidx.core.app.ActivityCompat import com.google.android.gms.location.* const val TAG = "Location" class MainActivity : AppCompatActivity() { var locEnable = false //caso não ajam permissões lateinit var fLoc : FusedLocationProviderClient override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) fLoc = FusedLocationProviderClient(this) } override fun onResume() { super.onResume() startLocationServices(true) //para poupanças de energia, ativar o mais tarde possivel } override fun onPause() { super.onPause() //para poupanças de energia, desactivar o mais cedo possivel if(locEnable) { locEnable = false } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) //se tivermos as permissões //locationM.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 100f, this) if(requestCode == 1289) { startLocationServices(false) } } fun startLocationServices(askPerm : Boolean) { //1ºprovider, 2º intervalo de segundos,3º distancia minima ,4º o listener //se for for telemovel NETWORK_PROVIDER //dar permissões: ACCESS_FINE_LOCATION //e pedir em runtime, add permissions check if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { //pedir as permissões //&& ou || if (askPerm) { ActivityCompat.requestPermissions(this, arrayOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION), 1289) //1289 um valor qualquer! } else { finish() } return } val locRequest = LocationRequest().apply { interval = 4000 priority = LocationRequest.PRIORITY_HIGH_ACCURACY //ocultar para ja estes dois: //fastestInterval = 2000 //maxWaitTime = 10000 //numUpdates /? a ver, quantidade de updates } //activar locEnable = true fLoc.requestLocationUpdates(locRequest, locationCallBack, null) } } //função locationCallBack var locationCallBack = object : LocationCallback() { override fun onLocationResult(p0: LocationResult?) { Log.i(TAG, "onLocationAvailability: ") //locations é uma lista de localizações p0?.locations?.forEach { Log.i(TAG, "onLocationAvailability: ${it.latitude} ${it.longitude}") } } }
Com esta versão são feitos updates mais rapidamente, com a ajuda do LocationRequest.PRIORITY_HIGH_ACCURACY
este é o método recomendado pela google..