sessão 5 – Gestão básica de threads em Win32
Bibliografia de apoio:
Capítulos 6 e 8 do Livro Windows System Programming (da bibliografia)
MSDN:
Synchronization Objects https://docs.microsoft.com/pt-pt/windows/win32/sync/synchronization-objects
Wait Functions https://msdn.microsoft.com/en-us/library/windows/desktop/ms687069(v=vs.85).aspx
Time Functions https://docs.microsoft.com/en-us/windows/win32/sysinfo/time-functions
Criar e sincronizar threads:
modelo de exclusão mutua ( com mutexes, um semáforo simplificado )
existe uma zona de memória que é partilhada entre threads mas que deve ser usada apenas quando está livre e trancada quando está ocupada (secção critica)
e serve para o mesmo processo ou entre processos
uma thread suspensa podem voltar com ResumeThread( HANDLE )
#include <windows.h> #include <tchar.h> #include <io.h> #include <fcntl.h> #include <stdio.h> #define TAM 200 #define MAX_THREADS 2 BOOL sairThread = FALSE; typedef struct { DWORD total_soma; int lim_inf, lim_sup; } dados_thread; DWORD WINAPI SomaPares(LPVOID lpParametro) { dados_thread* dados = (dados_thread*) lpParametro; _tprintf(TEXT("Sou a thread %d somar pares de %d a %d\n"), GetCurrentThreadId(), dados->lim_inf, dados->lim_sup); for (int i = dados->lim_inf; i <= dados->lim_sup; i++) { if (i % 2 == 0){ dados->total_soma += i; } if (i % 200 == 0){ Sleep(1000); } } return 0; } int _tmain(int argc, TCHAR* argv[]) { HANDLE hThreadArray[MAX_THREADS]; //handle threads dados_thread dado[MAX_THREADS]; DWORD tid[MAX_THREADS], resultado; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif //pedir aqui ao utilizador os intervalos _tprintf(TEXT("qual o valor MIN do 1º intervalo\n")); wscanf_s(TEXT("%d"), &dado[0].lim_inf); _tprintf(TEXT("qual o valor MAX do 1º intervalo\n")); wscanf_s(TEXT("%d"), &dado[0].lim_sup); _tprintf(TEXT("qual o valor MIN do 2º intervalo\n")); wscanf_s(TEXT("%d"), &dado[1].lim_inf); _tprintf(TEXT("qual o valor MAX do 2º intervalo\n")); wscanf_s(TEXT("%d"), &dado[1].lim_sup); dado[0].total_soma = dado[1].total_soma = 0; hThreadArray[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SomaPares, &dado[0], 0, &(tid[0])); hThreadArray[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SomaPares, &dado[1], 0, &(tid[1])); if (hThreadArray[0] == NULL || hThreadArray[1] == NULL) { _tprintf(TEXT("nada de threads")); return -1; } //esperar pela primeira para terminar resultado = WaitForMultipleObjects(2, hThreadArray, FALSE, INFINITE); if(resultado == WAIT_OBJECT_0) { _tprintf(TEXT("resultado da thread[%d]: %d\n"), tid[0], dado[0].total_soma); _tprintf(TEXT("resultado da thread[%d]: %d\n"), tid[1], dado[1].total_soma); } CloseHandle(hThreadArray[0]); CloseHandle(hThreadArray[1]); return 0; }
como medir o tempo de execução de um programa no windows?
#include <stdio.h> #include <stdlib.h> #include <windows.h> int main(void) { LARGE_INTEGER frequencia, inicio, fim; double intervalo; QueryPerformanceFrequency(&frequencia); // obter ticks por segundo QueryPerformanceCounter(&inicio); //obter ticks iniciais //executar o codigo QueryPerformanceCounter(&fim); // obter ticks finais intervalo = (double)(fim.QuadPart - inicio.QuadPart) / frequencia.QuadPart; //obter tempo em segundos printf("O codigo demorou %.3f segundos a executar.\n", intervalo); return 0; }
#include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #define MAX_THREADS 20 typedef struct { unsigned int limiteBaixo; unsigned int limiteAlto; unsigned int* contadorPartilhado; } TDados; DWORD WINAPI ContaMultiplosTres(LPVOID lmParam) { unsigned int i; TDados* data = (TDados*)lmParam; for (i = data->limiteBaixo; i <= data->limiteAlto; i++) { if (i % 3 == 0) { (*(data->contadorPartilhado))++; } } return 0; } int _tmain(int argc, TCHAR* argv[]) { HANDLE hThreads[MAX_THREADS]; TDados tdados[MAX_THREADS]; unsigned int numeroThreads, i, limSuperior = 400000000, contador = 0; LARGE_INTEGER tickSegundo, inicio, fim; double duracao; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif if (!QueryPerformanceCounter(&tickSegundo)) { _tprintf(TEXT("Erro: obter ticks por segundo.\n")); } _tprintf(TEXT("\nNumero de threads (max %d)->"), MAX_THREADS); _tscanf_s(TEXT("%u"), &numeroThreads); //lançar as threads (suspensas) for (i = 0; i < numeroThreads; i++) { tdados[i].limiteBaixo = 1 + (limSuperior / numeroThreads) * i; tdados[i].limiteAlto = (limSuperior / numeroThreads) * (i + 1); tdados[i].contadorPartilhado = &contador; _tprintf(TEXT("thread %u: %u a %u\n"), i, tdados[i].limiteBaixo, tdados[i].limiteAlto); hThreads[i] = CreateThread(NULL, 0, ContaMultiplosTres, &tdados[i], CREATE_SUSPENDED, NULL); } //começar o cronometro, ativar as threadas QueryPerformanceCounter(&inicio); for (i = 0; i < numeroThreads; i++) //dividir os intervalos { ResumeThread(hThreads[i]); //ativar as threads } WaitForMultipleObjects(numeroThreads, hThreads, TRUE, INFINITE); //esperar que as threads terminem //esperar que as threads terminem, ler o cronometro QueryPerformanceCounter(&fim); duracao = (double)(fim.QuadPart - inicio.QuadPart) / tickSegundo.QuadPart; _tprintf(TEXT("Contados %u numeros em %lf segundos.\n"), contador, duracao); for (i = 0; i < numeroThreads; i++) { CloseHandle(hThreads[i]); } //existe um problema de sincronização //falta sincronização das threads //o numero ideal de threads é igual ao numero de cores do processador //4 cores, 4 threads //4 cores, 5 thread, fica mais lento... //o escalonamento de processos.. return 0; }
//usar mutexes para garantir acesso em exlucsão mutua ao contador global //createMutexA, waitForSingleObject, ReleaseMutex, CloseHandle #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #define MAX_THREADS 20 typedef struct { unsigned int limiteBaixo; unsigned int limiteAlto; unsigned int* contadorPartilhado; //receber o mutex HANDLE mutexPartilhado; } TDados; DWORD WINAPI ContaMultiplosTres(LPVOID lmParam) { unsigned int i; TDados* data = (TDados*)lmParam; for (i = data->limiteBaixo; i <= data->limiteAlto; i++) { if (i % 3 == 0) { //esperar no mutex, incrementar, libertar WaitForSingleObject(data->mutexPartilhado, INFINITE); (*(data->contadorPartilhado))++; ReleaseMutex(data->mutexPartilhado); } } return 0; } int _tmain(int argc, TCHAR* argv[]) { HANDLE hThreads[MAX_THREADS]; TDados tdados[MAX_THREADS]; unsigned int numeroThreads, i, limSuperior = 4000000, contador = 0; LARGE_INTEGER tickSegundo, inicio, fim; double duracao; //o mutex HANDLE mutex; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif if (!QueryPerformanceCounter(&tickSegundo)) { _tprintf(TEXT("Erro: obter ticks por segundo.\n")); } _tprintf(TEXT("\nNumero de threads (max %d)->"), MAX_THREADS); _tscanf_s(TEXT("%u"), &numeroThreads); //criar o mutex mutex = CreateMutex(NULL, FALSE, NULL); //lançar as threads (suspensas) for (i = 0; i < numeroThreads; i++) { tdados[i].limiteBaixo = 1 + (limSuperior / numeroThreads) * i; tdados[i].limiteAlto = (limSuperior / numeroThreads) * (i + 1); tdados[i].contadorPartilhado = &contador; //passar o mutex tdados[i].mutexPartilhado = mutex; _tprintf(TEXT("thread %u: %u a %u\n"), i, tdados[i].limiteBaixo, tdados[i].limiteAlto); hThreads[i] = CreateThread(NULL, 0, ContaMultiplosTres, &tdados[i], CREATE_SUSPENDED, NULL); } //começar o cronometro, ativar as threadas QueryPerformanceCounter(&inicio); for (i = 0; i < numeroThreads; i++) //dividir os intervalos { ResumeThread(hThreads[i]); //ativar as threads } WaitForMultipleObjects(numeroThreads, hThreads, TRUE, INFINITE); //esperar que as threads terminem //esperar que as threads terminem, ler o cronometro QueryPerformanceCounter(&fim); duracao = (double)(fim.QuadPart - inicio.QuadPart) / tickSegundo.QuadPart; _tprintf(TEXT("Contados %u numeros em %lf segundos.\n"), contador, duracao); for (i = 0; i < numeroThreads; i++) { CloseHandle(hThreads[i]); } //fechar o mutex CloseHandle(mutex); //agora os resultados já nao vão variar, vão ser sempre os corretos //o tempo de execução aumenta return 0; }
critical section: recurso partilhado (em vez de mutexes)
mecanismo de controlo de acesso: CriticalSections, espécie de mutex, que tambem controla o acesso à secção critica
é uma espera ativa, que vai verificar um determinado numero de vezes se pode entrar
criticalsection é como um mini-mutex optimizado para threads dentro do mesmo processo
a thread tem um periodo de espera ativa à entrada da criticalsection, em que vai consultado para ver se pode entrar um numero de vezes (no max spin count)
pode aliviar o esforço de sinalização entre threads principalmente para spin count baixos
eficicente quando o recurso fica bloqueado por períodos curtos: assim não é preciso suspender e depois sinalizar threads tantas vezes
#include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #define MAX_THREADS 20 typedef struct { unsigned int limiteBaixo; unsigned int limiteAlto; unsigned int* contadorPartilhado; //referencia da criticalSection CRITICAL_SECTION * cirticalSectionPartilhada; } TDados; DWORD WINAPI ContaMultiplosTres(LPVOID lmParam) { unsigned int i; TDados* data = (TDados*)lmParam; for (i = data->limiteBaixo; i <= data->limiteAlto; i++) { if (i % 3 == 0) { //entrada na critical section EnterCriticalSection(data->cirticalSectionPartilhada); (*(data->contadorPartilhado))++; //saida na critical section LeaveCriticalSection(data->cirticalSectionPartilhada); } } return 0; } int _tmain(int argc, TCHAR* argv[]) { HANDLE hThreads[MAX_THREADS]; TDados tdados[MAX_THREADS]; unsigned int numeroThreads, i, limSuperior = 4000000, contador = 0; LARGE_INTEGER tickSegundo, inicio, fim; double duracao; //declaração variavel critical section CRITICAL_SECTION criticalSection; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif if (!QueryPerformanceCounter(&tickSegundo)) { _tprintf(TEXT("Erro: obter ticks por segundo.\n")); } _tprintf(TEXT("\nNumero de threads (max %d)->"), MAX_THREADS); _tscanf_s(TEXT("%u"), &numeroThreads); //inicializar da criticalsection if(!InitializeCriticalSectionAndSpinCount(&criticalSection, 400)) //spinout 400, normal.. { return 0; } //lançar as threads (suspensas) for (i = 0; i < numeroThreads; i++) { tdados[i].limiteBaixo = 1 + (limSuperior / numeroThreads) * i; tdados[i].limiteAlto = (limSuperior / numeroThreads) * (i + 1); tdados[i].contadorPartilhado = &contador; //passar o ponteiro da critical section tdados[i].cirticalSectionPartilhada = &criticalSection; _tprintf(TEXT("thread %u: %u a %u\n"), i, tdados[i].limiteBaixo, tdados[i].limiteAlto); hThreads[i] = CreateThread(NULL, 0, ContaMultiplosTres, &tdados[i], CREATE_SUSPENDED, NULL); } //começar o cronometro, ativar as threadas QueryPerformanceCounter(&inicio); for (i = 0; i < numeroThreads; i++) //dividir os intervalos { ResumeThread(hThreads[i]); //ativar as threads } WaitForMultipleObjects(numeroThreads, hThreads, TRUE, INFINITE); //esperar que as threads terminem //esperar que as threads terminem, ler o cronometro QueryPerformanceCounter(&fim); duracao = (double)(fim.QuadPart - inicio.QuadPart) / tickSegundo.QuadPart; _tprintf(TEXT("Contados %u numeros em %lf segundos.\n"), contador, duracao); for (i = 0; i < numeroThreads; i++) { CloseHandle(hThreads[i]); } //remover a criticalsection DeleteCriticalSection(&criticalSection); //melhoria de desempenho muito consideravel // return 0; }
eventos: atuam como flags para assinalar à thread se pode continuar ou não
podem ser usados para outros cenários que não threads
a usar: CreateEventA, SetEvent
#include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #define MAX_THREADS 20 typedef struct { unsigned int limiteBaixo; unsigned int limiteAlto; unsigned int* contadorPartilhado; CRITICAL_SECTION* cirticalSectionPartilhada; //referência a evento HANDLE eventoPartilhado; } TDados; DWORD WINAPI ContaMultiplosTres(LPVOID lmParam) { unsigned int i; TDados* data = (TDados*)lmParam; //aguardar evento antes de começar, a thread fica à espera que o evento seja TRUE WaitForSingleObject(data->eventoPartilhado, INFINITE); for (i = data->limiteBaixo; i <= data->limiteAlto; i++) { if (i % 3 == 0) { EnterCriticalSection(data->cirticalSectionPartilhada); (*(data->contadorPartilhado))++; LeaveCriticalSection(data->cirticalSectionPartilhada); } } return 0; } int _tmain(int argc, TCHAR* argv[]) { HANDLE hThreads[MAX_THREADS]; TDados tdados[MAX_THREADS]; unsigned int numeroThreads, i, limSuperior = 4000000, contador = 0; LARGE_INTEGER tickSegundo, inicio, fim; double duracao; CRITICAL_SECTION criticalSection; //evento HANDLE evento; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif if (!QueryPerformanceCounter(&tickSegundo)) { _tprintf(TEXT("Erro: obter ticks por segundo.\n")); } _tprintf(TEXT("\nNumero de threads (max %d)->"), MAX_THREADS); _tscanf_s(TEXT("%u"), &numeroThreads); if (!InitializeCriticalSectionAndSpinCount(&criticalSection, 400)) //spinout 400, normal.. { return 0; } //criar o evento evento = CreateEvent(NULL, TRUE, FALSE, NULL); for (i = 0; i < numeroThreads; i++) { tdados[i].limiteBaixo = 1 + (limSuperior / numeroThreads) * i; tdados[i].limiteAlto = (limSuperior / numeroThreads) * (i + 1); tdados[i].contadorPartilhado = &contador; tdados[i].cirticalSectionPartilhada = &criticalSection; //passar o evento para a thread tdados[i].eventoPartilhado = evento; _tprintf(TEXT("thread %u: %u a %u\n"), i, tdados[i].limiteBaixo, tdados[i].limiteAlto); hThreads[i] = CreateThread(NULL, 0, ContaMultiplosTres, &tdados[i], CREATE_SUSPENDED, NULL); } QueryPerformanceCounter(&inicio); //gerar evento para threads avançarem SetEvent(evento); //o ResumeThread já não é necessário WaitForMultipleObjects(numeroThreads, hThreads, TRUE, INFINITE); //esperar que as threads terminem QueryPerformanceCounter(&fim); duracao = (double)(fim.QuadPart - inicio.QuadPart) / tickSegundo.QuadPart; _tprintf(TEXT("Contados %u numeros em %lf segundos.\n"), contador, duracao); for (i = 0; i < numeroThreads; i++) { CloseHandle(hThreads[i]); } DeleteCriticalSection(&criticalSection); //termina a o evento CloseHandle(evento); //as threds começam todas ao mesmo tempo, o tempo vai demorar //este mecanismo permite desbloquear um processo return 0; }
Magallanes_ICC :
Está a decorrer um “Open Call Programa de Apoio ao Empreendedorismo Criativo” onde se inclui a apresentação para Jogos/ softwares educativos
+infos(oficial): https://arterialab.uevora.pt/criativos/
sessão 4 – Gestão básica de threads em Win32
Bibliografia de apoio:
Capítulos 6, 7 e 8 do Livro Windows System Programming (da Bibliografia) (pags. 194-195, 223-232, 243-245,252-253, 279-281)
MSDN:
Acerca de threads e processos https://docs.microsoft.com/en-us/windows/win32/procthread/about-processes-and-threads
Gestão de threads https://docs.microsoft.com/en-us/windows/win32/procthread/multiple-threads
Criação de threads (exemplo) https://docs.microsoft.com/en-us/windows/win32/procthread/creating-threads
Funções básicas de espera (WaitforSingleObject / WaitForMultipleObjects) https://docs.microsoft.com/en-us/windows/win32/sync/wait-functions#single-object-waitfunctions
Mutexes https://docs.microsoft.com/en-us/windows/win32/sync/using-mutex-objects
Para criar uma thread:
sabe o que é necessário que seja executado em simultâneo para além da thread principal (e sem interromper a thread principal)
CreateThread( NULL, //segurança: descritores de segurança, só o utilizador é que pode interagir com a thread 0, //tamanho máximo da pilha, cria quantas variáveis locais que ela quiser criar (e só pertencem a ela) FuncaoThread, //nome da função para correr em simultâneo (podemos passar um parâmetro para esta função) &total, //LPVOID parametro, ponteiro para void, uso referencia para poder obter o valor atualizado 0, //eu pretendo que a thread comece a correr, onde 0 é para começar a correr (por oposição de a deixar suspensa, com o resume: CREATE_SUSPENDED) &threadId //opcional: é o threadID, é um DWORD ou colocar NULL se não quiser usar );
//uma unica thread #include <windows.h> #include <tchar.h> #include <io.h> #include <fcntl.h> #include <stdio.h> #define TAM 200 DWORD WINAPI FuncaoThread(LPVOID parametro) { int* dados = (int*)parametro; for (int i = 0; i <= 100; i++) { (*dados) += i; if(i%15== 0) { Sleep(1000); } } return 0; } int _tmain(int argc, TCHAR* argv[]) { DWORD t1; int total = 0; HANDLE hThread; hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) FuncaoThread, &total, 0, &t1); //sleep() //WaitForSingleObject() if(hThread != NULL) { WaitForSingleObject(hThread, INFINITE); //esperar até terminar ou dar um tempo _tprintf(TEXT("resultado da thread: %d"), total); //getchar(); CloseHandle(hThread); }else { _tprintf(TEXT("nada de threads")); } return 0; }
DWORD WaitForMultipleObjects( DWORD nCount, //numero de handles do array const HANDLE *lpHandles, //array de handles BOOL bWaitAll, // true se espero por todas DWORD dwMilliseconds //indicar se espero pelo fim de todas );
#include <windows.h> #include <tchar.h> #include <io.h> #include <fcntl.h> #include <stdio.h> #define TAM 200 #define MAX_THREADS 2 BOOL sairThread = FALSE; typedef struct { int total, limite_inferior, limite_superior; } dados_thread; DWORD WINAPI FuncaoThread(LPVOID parametro) { dados_thread * dados = (dados_thread *) parametro; //ponteiro para a estrutura for (int i = dados->limite_inferior; i <= dados->limite_superior; i++) { dados->total += i; if (i % 15 == 0) { Sleep(1000); if(sairThread) { break; } } } return 0; } DWORD WINAPI comandos(LPVOID parametro) { TCHAR comando[TAM]; while(!sairThread) { _tprintf(TEXT("Escrever 'sair' para terminar.. \n")); wscanf_s(TEXT("%s"),comando, TAM-1); if(wcscmp(comando, TEXT("sair"))==0) { sairThread = TRUE; } } return 0; } int _tmain(int argc, TCHAR* argv[]) { DWORD t1, t2; dados_thread dado[MAX_THREADS]; HANDLE hThreadArray[MAX_THREADS]; //handle threads #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif dado[0].limite_inferior = 0; dado[0].limite_superior = 100; dado[0].total = 0; dado[1].limite_inferior = 0; dado[1].limite_superior = 100; dado[1].total = 0; hThreadArray[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FuncaoThread, &dado[0], 0, &t1); hThreadArray[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FuncaoThread, &dado[1], 0, &t2); CreateThread(NULL, 0, comandos, NULL, 0, NULL); if (hThreadArray[0] == NULL || hThreadArray[1] == NULL) { _tprintf(TEXT("nada de threads")); ExitProcess(3); } WaitForMultipleObjects(2, hThreadArray, TRUE, INFINITE); _tprintf(TEXT("resultado da thread[%d]: %d\n"), t1, dado[0].total); _tprintf(TEXT("resultado da thread[%d]: %d\n"), t2, dado[1].total); CloseHandle(hThreadArray[0]); CloseHandle(hThreadArray[1]); return 0; }
sessão 3 – DLL – Bibliotecas de ligação dinâmica
Bibliografia de apoio:
Material das aulas teóricas e incluindo documentos de apoio ao tópico de bibliotecas dinâmicas
Capítulo 5 do Livro Windows System Programming (da Bibliografia) (pags. 167 em diante)
MSDN:
Run-Time Dynamic Linking (Overview geral) https://docs.microsoft.com/en-us/windows/win32/dlls/run-time-dynamic-linking
Using Run-Time Dynamic Linking (Inclui exemplos) https://docs.microsoft.com/en-us/windows/win32/dlls/using-run-time-dynamic-linking
Referência do API para lidar com DLL (libloaderapi) https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/
Manipulação do registry: cria uma chave (se não existir), e abre a chave
#include <windows.h> #include <tchar.h> #include <io.h> #include <fcntl.h> #include <stdio.h> #define TAM 200 int _tmain(int argc, TCHAR* argv[]) { HKEY chave; TCHAR nome[TAM] = TEXT("SOFTWARE\\SO2"); TCHAR par_nome[TAM] = TEXT("1º autor"); TCHAR par_valor[TAM] = TEXT("João Sá"); DWORD o_que_aconteceu; DWORD n_vezes = 1; DWORD tam = sizeof(DWORD); #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif if (RegCreateKeyEx(HKEY_CURRENT_USER, nome, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &chave, &o_que_aconteceu) == ERROR_SUCCESS) { if (o_que_aconteceu == REG_CREATED_NEW_KEY) { _tprintf(TEXT("chave %s criada com sucesso !"), nome); RegSetValueEx(chave, par_nome, 0, REG_SZ, (LPBYTE)par_valor, (_tcslen(par_valor) + 1) * sizeof(DWORD)); RegSetValueEx(chave, TEXT("Número de execuções"), 0, REG_DWORD, (LPBYTE)&n_vezes, sizeof(DWORD)); } else { _tprintf(TEXT("chave %s aberta com sucesso\n"), nome); RegQueryValueEx(chave, TEXT("Número de execuções"), 0, NULL, (LPBYTE)&n_vezes, &tam); n_vezes++; _tprintf(TEXT("Esta é a %d vez que o programa é executado"), n_vezes); RegSetValueEx(chave, TEXT("Número de execuções"), 0, REG_DWORD, (LPBYTE)&n_vezes, sizeof(DWORD)); } return -1; } else { _tprintf(TEXT("Erro ao abrir/criar chave %s"), nome); } RegCloseKey(chave); return 0; }
Referência do API para lidar com DLL: LINK
Fazer a leitura pelo modo implícito na arquitetura x86
//base.c #include "principal.h" #include <windows.h> #include <tchar.h> #include <io.h> #include <fcntl.h> #include <stdio.h> int _tmain(int argc, TCHAR* argv[]) { double local; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif //usar a variavel DLL do { _tprintf(TEXT("Valor atual para a variavel global da DLL:%lf\nNovo valor:"), factor); _tscanf_s(TEXT("%lf"), &factor); _tprintf(TEXT("Valor atual para a variavel global da DLL:%lf\n"), factor); _tprintf(TEXT("Valor a passar como argumento à função da DLL:")); _tscanf_s(TEXT("%lf"), &local); _tprintf(TEXT("Resultado da função da DLL:%lf\n"), applyFactor(local)); } while (factor != -1); return 0; }
//base.h #pragma once #include <windows.h> #ifdef SO2F3DLL_EXPORTS #define DLL_IMP_API __declspec(dllexport) #else #define DLL_IMP_API __declspec(dllimport) #endif DLL_IMP_API double factor; //variavel global, serve para cada processo //cada processo acede à sua DLL_IMP_API double applyFactor(double v); //funcao
carregar para o projeto o ficheiro .LIB, e colocar o ficheiro DLL na pasta do executável
Fazer a leitura pelo modo explicito
_mais independente
_mais controlo ao programador (pode ser usada apenas na altura em que preciso: da variável, da função, .. que está na DLL)
_só preciso quando for executar o programa
_não vai ser necessário o ficheiro h com a “estrutura” que consta no DLL
#include <windows.h> #include <tchar.h> #include <io.h> #include <fcntl.h> #include <stdio.h> #define TAM 256 int obterValorUtilizador() { int valor = 0; TCHAR buffer[TAM] = _TEXT(""); //obter o nome da chave _tprintf(_TEXT("introduza um valor: ")); fflush(stdin); _fgetts(buffer, TAM, stdin); //retirar o \n buffer[_tcsclen(buffer) - 1] = '\0'; valor = _tstoi(buffer); return valor; } int _tmain(int argc, TCHAR* argv[]) { int a, b; int sair = 0; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif _tprintf(TEXT("Valor inicial da variavel da DLL \n \n")); double (*ptrVar)(double) = NULL; double (*ptrFunc)(double) = NULL; HINSTANCE hLib = LoadLibrary(TEXT("so2x86.dll")); if (hLib != NULL) { ptrFunc = (double(*)(double))GetProcAddress(hLib, "applyFactor"); ptrVar = (double(*)(double))GetProcAddress(hLib, "factor"); _tprintf(TEXT("parte2\n \n")); if (ptrVar != NULL &amp;&amp; ptrFunc != NULL) { _tprintf(TEXT("parte3\n \n")); _tprintf(_TEXT("Valor inicial da variavel da DLL: %f\n"), *((double*)ptrVar)); do { a = 0; b = 0; _tprintf(_TEXT("vairavel (a)\n")); a = obterValorUtilizador(); if (a == 1) { sair = 1; break; } *((double*)ptrVar) = (double)a; _tprintf(_TEXT("Valor novo da variavel da DLL: %f\n"), *((double*)ptrVar)); b = obterValorUtilizador(); _tprintf(_TEXT("Valor novo da variavel da DLL: %f\n"), ptrFunc(b)); } while (sair != 1); } if (hLib != NULL) { FreeLibrary(hLib); } } return 0; }
Criar uma DLL
Mudar o projeto para concretizar uma DLL e não um ficheiro executavel (nas propriedades do projeto)
O visual studio sugere uma variável de nome _WINDLL
//source.c #include <tchar.h> #include <stdlib.h> //#define SO2F3DLL_EXPORTS //vou usar a constante sugerida pelo VS #include <time.h> #include "Header.h" //deve ser feito depois do define double varGlobal = 4; int varInternaDLL = 5; double aplicarFactor(double argumento) { _tprintf(_TEXT("esta é a minha DLL")); return varGlobal; } double geraAleatorio(int valor_min, int valor_max) { static int n_vezes = 1; if(n_vezes == 1) { srand(time(NULL)); n_vezes++; } return rand() % (valor_max - valor_min) + valor_min; }
#pragma once //header.h //diretivas para o importar e para o exportar //SO2F3DLL_EXPORTS é uma constante que fou ter que partilhar e vai para o .c //#ifdef SO2F3DLL_EXPORTS #ifdef _WINDLL #define DLL_IMP_API __declspec(dllexport) #else #define DLL_IMP_API __declspec(dllimport) #endif //porque quero que outros usem, e tem que ser exportado DLL_IMP_API double varGlobal; //não vai ser exportada int varInternaDLL; double geraAleatorio(int valor_min, int valor_max); DLL_IMP_API double aplicarFactor(double argumento);
sessão 2 – Criar, consultar, alterar e remover chaves do Registry.
Bibliografia de apoio:
Capítulo 3 do Livro Windows System Programming (da Bibliografia) (pags. 86-97)
MSDN:
Registry Functions: https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry-functions
32-bit and 64-bit Application Data in the Registry: https://docs.microsoft.com/en-us/windows/win32/sysinfo/32-bit-and-64-bit-application-data-in-the-registry?redirectedfrom=MSDN
Criação de processo e espera até terminar:
TCHAR comando[TAM] = TEXT("notepad-exe ficheiro.txt"); PROCESS_INFORMATION pi; STARTUPINFO si; ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); if (CreateProcess(NULL, comando, NULL, NULL, 0, 0, NULL, &si, &pi) { _tprintf(TEXT("processo com PID: %d foi lançado"), pi.dwProcessId); WaitForSingleObject(pi.hProcess, INFINITE); }
O editor do registry: registry editor (regedit)
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE (só admin, todos os programas que são instalados)
Para criar fica assim:
#include <windows.h> #include <tchar.h> #include <io.h> #include <fcntl.h> #include <stdio.h> #define TAM 200 int _tmain(int argc, TCHAR* argv[]) { HKEY chave; //handle para a chave depois de ser aberta/criada //caminho e nome da chave é criada SOFTWARE\\SO2\\chaveLab, uso da contrabarra "\\" TCHAR chave_nome[TAM] = TEXT("SOFTWARE\\SO2\\chaveLab"), par_nome[TAM], par_valor[TAM]; DWORD resultado; //com o que aconteceu com a chave #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif //para criar ou abrir uma chave do registry //RegCreateKeyEx retorna um valor se a chave foi criada/aberta com sucesso if(RegCreateKeyEx( //HKEY hKey, HKEY_CURRENT_USER, //nome da chave //LPCTSTR lpSubKey, chave_nome, //DWORD Reserved, 0, //LPTSTR lpClass, NULL, //DWORD dwOptions, longevidade da chave REG_OPTION_NON_VOLATILE, //REGSAM samDesired, KEY_ALL_ACCESS, //const LPSECURITY_ATTRIBUTES lpSecurityAttributes, segurança da chave NULL, // PHKEY phkResult, &chave, //LPDWORD lpdwDisposition &resultado ) != ERROR_SUCCESS) { _tprintf(TEXT("chave não foi criada nem aberta! ERRO!")); return -1; } if(resultado == REG_CREATED_NEW_KEY) { _tprintf(TEXT("a chave foi criada: %s"), chave_nome); }else { _tprintf(TEXT("a chave foi criada: %s"), chave_nome); } RegCloseKey(chave); //importante: fechar sempre o Handle da chave return 0; }
Os atributos de uma chave são: nome, tipo, e valor
#include <windows.h> #include <tchar.h> #include <io.h> #include <fcntl.h> #include <stdio.h> #define TAM 200 int _tmain(int argc, TCHAR* argv[]) { HKEY chave; //handle para a chave depois de ser aberta/criada //caminho e nome da chave é criada SOFTWARE\\SO2\\chaveLab, uso da contrabarra "\\" TCHAR chave_nome[TAM] = TEXT("SOFTWARE\\SO2\\chaveLab"); TCHAR par_nome[TAM] = TEXT("1º atributo"); TCHAR par_valor[TAM] = TEXT("É o novo valor"); DWORD resultado; //com o que aconteceu com a chave #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif //para criar ou abrir uma chave do registry //RegCreateKeyEx retorna um valor se a chave foi criada/aberta com sucesso if(RegCreateKeyEx( //HKEY hKey, HKEY_CURRENT_USER, //nome da chave //LPCTSTR lpSubKey, chave_nome, //DWORD Reserved, 0, //LPTSTR lpClass, NULL, //DWORD dwOptions, longevidade da chave REG_OPTION_NON_VOLATILE, //REGSAM samDesired, KEY_ALL_ACCESS, //const LPSECURITY_ATTRIBUTES lpSecurityAttributes, segurança da chave NULL, // PHKEY phkResult, &chave, //LPDWORD lpdwDisposition &resultado ) != ERROR_SUCCESS) { _tprintf(TEXT("chave não foi criada nem aberta! ERRO!")); return -1; } if(resultado == REG_CREATED_NEW_KEY) { _tprintf(TEXT("a chave foi criada: %s"), chave_nome); }else { _tprintf(TEXT("a chave foi aberta: %s"), chave_nome); } //criar oar nomeatributo = valor (1 por cada chamada da função) if(RegSetValueEx( //HKEY hKey, chave, //LPCSTR lpValueName, par_nome, //DWORD Reserved, 0, //DWORD dwType, //tipo: string (REG_SZ), inteiro (REG_WORD), bloco de bytes (REG_BINARY) REG_SZ, //const BYTE * lpData, //ponteiro para void, dá para tudo como o malloc (const LPCBYTE /*igual c const BYTE **/) par_valor, //DWORD cbData sizeof(TCHAR) * (_tcsclen(par_valor)+1) //+1 para o /0, para ler o /0 ) != ERROR_SUCCESS) { _tprintf(TEXT("Atributo %s não foi alterado nem criado ERRO\n"), par_nome); } //concatenar qq tipos de dados e guardar numa string //_strpintf_s (par_valor, TAM, TEXT("%s %d"), par_nome, tamanho); //consultar valor de um atributo //strcpy(par_nome, TEXT("2º atributo ")); //-> caso fosse necessário escrever a string em c depois de criada par_valor[0] = '\0'; DWORD tamanho = sizeof(par_valor); if(RegQueryValueEx( // HKEY hKey, chave, // LPCTSTR lpValueName, par_nome, //LPDWORD lpReserved, //é para consulta fica a NULL 0, //LPDWORD lpType, NULL, //LPBYTE lpData, (LPCBYTE)par_valor, //LPDWORD lpcbData &tamanho ) != ERROR_SUCCESS) { _tprintf(TEXT("Atributo %s não foi encotnrado!! ERRO\n"), par_nome); }else { _tprintf(TEXT("Atributo encontrado com o valor: %s e tamanho %d"), par_valor, tamanho); } DWORD valor_inteiro = 1022; valor_inteiro *= 2; _tcscpy_s(par_nome, TAM, TEXT("atributo inteiro")); if (RegSetValueEx( chave, par_nome, 0, REG_DWORD, (LPCBYTE) &valor_inteiro, sizeof(DWORD) ) != ERROR_SUCCESS) { _tprintf(TEXT("Atributo %s não foi encotnrado!! ERRO\n"), par_nome); } _tcscpy_s(par_nome, TAM, TEXT("atributo binario")); if (RegSetValueEx( chave, par_nome, 0, REG_BINARY, (LPCBYTE) &valor_inteiro, sizeof(DWORD) ) != ERROR_SUCCESS) { _tprintf(TEXT("Atributo %s não foi encotnrado!! ERRO\n"), par_nome); } RegCloseKey(chave); //importante: fechar sempre o Handle da chave return 0; }
sessão 1 – Trabalhar com caracteres UNICODE na consola, Criar processos utilizando a Windows API e esperar pelo seu término.
Bibliografia de apoio:
Capítulos 2 e 6 do Livro Windows System Programming (da Bibliografia) (pgs. 34-37, 181-187, 192-195)
MSDN: Strings & Unicode e Processes
#include <stdio.h> int main() { printf("Até amanhã\n"); getchar(); return 0; }
ou Use Unicdoe Character Set
ou Use Multi-Byte Character Set
apesar de estar em Use Unicdoe Character Set não é suficiente para escrever os caracteres especiais, vai ser necessário
#include <stdio.h> #include <windows.h> #include <tchar.h> #include <stdlib.h> #include <time.h> #include <fcntl.h> #include <io.h> #define MAX 256 int _tmain(int argc, TCHAR* argv[]) { //argc -> numero de argumentos //argv -> argumentos ////TCHAR vou aceitar argumentos com caracteres especiais TCHAR result[MAX] = TEXT("Olá! Este programa ainda não representa UNICODE\n"); #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); //#include <fcntl.h> #include <io.h> _setmode(_fileno(stdout), _O_WTEXT); //#include <fcntl.h> #include <io.h> _setmode(_fileno(stderr), _O_WTEXT); //#include <fcntl.h> #include <io.h> #endif _tprintf(TEXT("Frase:%s Tamanho:%d (caractares) %d (bytes)\n"), result, _tcslen(result), _tcslen(result) * sizeof(TCHAR)); //mas se fossem funções que não trabalhem com caracteres não é necessário srand(time(NULL)); int aleatorio = rand(); _gettchar(); return 0; } //manipulação de strings: //char -> 1 bytes (caracteres que estão na tabela ASCII) //a alternativa é usar o -> wchar (tem o dobro da capacidade, 2 bytes) e já permite armazenar caracteres UNICODE //ao invés de ter duas versões do progrma, apenas uso uma versão do código //TCHAR, não é um tipo nativo //#include <windows.h> //#include <tchar.h> //e desta forma não vou ter que fazer duas versões do programa: para sistemas mais antigos e mais modernos //_tmain -> main ou wamin //uma string fixa, cada letra ocupa 2 bytes -> L"Olá! Este programa ainda não representa UNICODE\n" //ou uso a macro TEXT("") ou _T("") -> "" para resolver as strings fixas //e vou usar também o _tprintf -> printf ou wprintf //_tcslen -> strlen //_gettchar -> getchar() //preparar a consola para mostrar caracteres wide: // _setmode(stdin, constante); // _setmode(stdout, constante); //calcular o tamanho real: // _tcslen(result)*sizeof(TCHAR) // não interessa se está ou não em unicode
#include <windows.h> #include <tchar.h> #include <fcntl.h> #include <io.h> #include <stdio.h> #define MAX 256 //LPTSTR -> TCHAR * //LPCTSTR -> const THACAR * int _tmain(int argc, LPTSTR argv[]) { TCHAR str[MAX], result[MAX] = TEXT("Olá! Este programa é para aceitar UNICODE. Insira \'fim\' para sair\n"); unsigned int i; //UNICODE: Por defeito, a consola Windows não processa caracteres wide. //A maneira mais fácil para ter esta funcionalidade é chamar _setmode: #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); #endif do { _tprintf(result); fflush(stdin); _fgetts(str, MAX, stdin); //fgets msdn -> _fgetts //Retirar \n str[_tcslen(str) - 1] = '\0'; //Maiúsculas for (i = 0; i < _tcslen(str); i++) str[i] = _totupper(str[i]); _stprintf_s(result, MAX, TEXT("Frase:%s, Tamanho:%d\n"), str, _tcslen(str)); } while (_tcsicmp(TEXT("FIM"), str)); return 0; } //setlocale -> para lidar com os caracteres especiais em maiúsculas
Os processos em Windows (child processes) CreateProcess (em linux era fork)
#include <windows.h> #include <tchar.h> #include <fcntl.h> #include <io.h> #include <stdio.h> #define MAX 256 int _tmain(int argc, LPTSTR argv[]) { TCHAR str[MAX], result[MAX] = TEXT("Olá! Este programa é para aceitar UNICODE. Insira \'fim\' para sair\n"); unsigned int i; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); #endif do { _tprintf(result); fflush(stdin); _fgetts(str, MAX, stdin); //fgets msdn -> _fgetts //Retirar \n str[_tcslen(str) - 1] = '\0'; //Maiúsculas for (i = 0; i < _tcslen(str); i++) str[i] = _totupper(str[i]); _stprintf_s(result, MAX, TEXT("Frase:%s, Tamanho:%d\n"), str, _tcslen(str)); } while (_tcsicmp(TEXT("FIM"), str)); TCHAR texto[256] = TEXT("notepad"); STARTUPINFO si; // PROCESS_INFORMATION pi; // //memset(); ZeroMemory(&si, sizeof(si)); // mesmo que o memset mas com tudo a zero ZeroMemory(&pi, sizeof(pi)); // resultado da função: pid do processo, pid da threat principal do processo, //e handle (ponteiro para estrutura) do processo, e handle para a thread principal //pid unico no sistema para cada processo, o handle podem ser vários, cada um com determinada permissão por exemplo si.cb = sizeof(STARTUPINFO); //tamanho da estrutura tem que estar preenchido, pelo menos esse!! .cb //criar um processo -> correr um programa que já foi compilado if (CreateProcess(NULL, // No module name (use command line): se fosse o proprio -> argv[0] ou GetModuleFileName //argv[1], // Command line texto, NULL, // Process handle not inheritable NULL, // Thread handle not inheritable FALSE, // Set handle inheritance to FALSE 0, // No creation flags NULL, // Use parent's environment block NULL, // Use parent's starting directory &si, // Pointer to STARTUPINFO structure &pi) // Pointer to PROCESS_INFORMATION structure ) { _tprintf(TEXT("processo foi lançado PID: %d THREAD: %d"), pi.dwProcessId, pi.dwThreadId ); }else { _tprintf("CreateProcess failed (%d).\n", GetLastError()); return; } //esperar pelo processo terminar em linux: wait WaitForSingleObject(pi.hProcess, INFINITE); //ms >0 ou então quando terminar INFINITE //se for 0 só para consulta return 0; }
Gestão de projetos de software, apontamentos
Programa:
1. Project planning
• Software estimation and scheduling
• Development of a software development plan
2. Quality management
• Quality plan
• Reviews and inspections
• Software testing
• Configuration management and version control
3. Software project management
• Requirements management
• Project monitoring and control
• Risk analysis and management
• Team management
4. Software development processes
• Process models and process measurements
software project problems?
1. Excessive schedule pressure
2. Changing needs
3. Lack of technical specifications
4. Lack of a documented project plan
5. Excessive and secondary innovations
6. Unclear requirements
7. Lack of scientific methods
8. Defects discovered too late
9. Unethical behavior
Why do software projects fail?
• People begin programming before they understand the problema
• The team has an unrealistic idea about how much work is involved.
• Defects are injected early but discovered late.
• Programmers have poor habits – and they don’t feel accountable for their work.
• Introduce software quality from the very beginning of the project
Vision & Scope (identificar quais as necessidades, e convencer o cliente a comprar, construído pelo business manager)
1. Problem Statement
• Project background (qual é o problema)
• Stakeholders (clientes de um restaurante, usam o tablet..)
• Users (quem vai usufruir, o cliente, o dono do restaurante)
2. Vision of the Solution
• Vision statement (como vai ser resolvido, …)
• List of features (o que vai ter a app, menus, quantidades…)
• Features that will not be developed
• Risks
• Assumptions (existem poucas opções…)
Documents lifecycle (gestão das versões)
Phase1: Project Preparation
• Stage 1.1 – Project Setup & Software Concept
o Deliverable D1.1.1 – Vision & Scope
o Deliverable D1.1.2 – Milestone M1.1 Report
o Milestone M1.1 – Scope Review
• Stage 1.2 – Project Planning
o Deliverable D1.2.1 – Software Development Plan
o Deliverable D1.2.2 – Quality Assurance Plan
o Deliverable D1.2.3 – Milestone M1.2 Report
o Milestone M1.2 – Plan Review
Phase2: Project Execution
• Stage 2.1 –Requirements Specification
o Deliverable D2.1.1 – Software Requirements Specification
o Deliverable D2.1.2 – Risk Plan
o Deliverable D2.1.3 – Acceptance Test Plan
o Deliverable D2.1.4 – Milestone M2.1 Report
o Milestone M2.1 – Software Requirements Review
• Stage 2.2 – Software Development
o Deliverable D2.2.1 – Software Architecture & Design
o Deliverable D2.2.2 – Milestone M2.2 Report
o Milestone M2.2 – Design Review
• Stage 2.3 – Software Acceptance
o Deliverable D2.3.1 – Acceptance Test Report
o Deliverable D2.3.2 – Quality Assessment Report
o Deliverable D2.3.1 – Milestone M2.3 Report
o Milestone M2.3 – Acceptance Review
Phase3: Project Closeup
• Stage 3.1 – Software Delivery
o Deliverable D3.1.1 – Post-Mortem Analysis
o Milestone M3.1 – Closeup
(que trabalho existe para fazer/descrição, quem vai fazer)
Uma lista de tarefas/módulos, divisão do trabalho
Engenharia
Software concept
Project planning
Project planning
WBS (work breakdown structure)
project estimation report (o que se vai fazer), tarefas pequenas, subdivididas,
Gestão
Project setup
Documents management
Change management
Meetings management
Ciclo de vida geral de um documento:
Quality assurance
Inspection
Reisk management
Testing
Deskcheck
Walkthrought
5. Estimation
Entra o gestor do projeto
Faz uso do vision and scope para construir o software develpment plan, onde surge a estimativa. Usando o modelo cascata, fazemos a estimativa completa
Estimativas vs espectativas no cliente (âmbito, custo, duração..)
Estimativa serve para negociar (custo e calendário), para prlanear o projeto, para controlar o projeto
Ou um work breakdown structure (WBS): é a subdivisão do trabalho em tarefas mais pequenas de trabalho, organizadas de forma hierárquica
Ou um product breakdown struture (PBS): é a subdivisão em tarefas mais pequenas; inclui o WBS; indicar um esforço para cada tarefa (o trabalho que foi efetivamente realizada)
Para isso é necessário:
• Saber os pressupostos de cada uma das tarefas
• Obter consenso dentro da equipa, discutir, conversar..
Tendo em consideração:
• O nível de confiança, o histórico/conhecimentos em tarefas semelhantes, experiência pessoal.
E as suposições? Cuidado com isto..
A estimativa é uma aproximação a uma “realidade”
Problemas relacionados com o estimar:
Complexidade dos sistemas VS trabalho repetitivo
Subestimar VS sobrestimar pode tornar o produto mais caro
Estimativas são objetivos de facto
Informação inadequada acerca das capacidades da organização
Requisitos pouco esclarecidos/claros
“Estimar com o que achamos que vamos conseguir fazer, sem preocupações com as horas que temos”
O que é estimado:
• o peso
• o esforço (horas)
• o calendário (uma unidade no calendário)
• o custo (euros)
O cone da incerteza
(quanto mais à frente for a fase do desenvolvimento melhor é a estimativa)
Técnicas como estimar:
“adivinhar” de forma estruturada
Experiência
Delphi groups
Analogias (olhar para projetos anteriores)
Bottom-up, estimar módulos mais pequenos, para cada componente
Ou o Planning poker
(existe um moderador, existe o dono do produto, e os que estimam)
1º o moderador lê a descrição dos requisitos, as linhas do WBS
2º o dono do produto responde às questões dos estimadores
3º cada um vai estimar com uma carta: 1 quer dizer uma hora, ou um ponto, … e coloca a carta virada para baixo (sem divulgar)
4º depois de todas as estimativas estarem indicadas, as cartas são voltadas para cima
5º só se a estimativa variar profundamente, os donos das cartas mais altas e mais baixas discutem as razões. Todos devem participar na discussão, dentro de um limite de tempo
E por fim quando se chegar ao consenso escreve-se a estimativa! O Project estimation report!
(não existe número 4.. e outras, existem poucas cartas reuniões mais rápidas, mais fácil chegar a um consenso, menos cartas mais consenso)
(mas e se uma tarefa durar mais tempo que a ultima carta 9?, dividir a tarefa em sub tarefas..)
6. Gestão da qualidade
Um produto com qualidade vai ao encontro das espectativas do cliente
Como obter a qualidade?
Os processos estão escritos
Manual da qualidade
Tem processos que seguem normas (certificações)
E os processos de qualidade:
O plano de qualidade é definido por projeto (seguindo o quality manual)
As tarefas de baixo:
são as tarefas extra, surgem no plano de qualidade rever os requisitos, rever o código, fazer testes ao código.. e por exemplo implicam um esforço, quanto mais critico é o software, mais esforço surge nesta parte e vai ter um custo maior do que escrever propriamente o código.
O plano de qualidade:
• Vão ter atributos (internamente[o que podemos fazer na empresa, o que interessa para a equipa, código fácil de compreender, é portável?, usar o mesmo código para outro sistema] e externamente[“não gastar bateria”, safety “ se alguém morrer por erros de código”], do ponto de vista do cliente, de fora da equipa)
o Exemplos de atributos (o que é mais importante para o projeto):
o As métricas?
De produto: tempo de execução, números de bugs, número de linhas de código, satisfação do utilizador
Do projeto: se mudaram os requisitos, requisitos estáveis, se seguiram os processos todos
Ajudam a medir a qualidade (tem o produto no fim a qualidade que era desejada ou não?)
• Vão ter processos (revisões, testes, qual a arquitetura, testes de performance)
• Vão ter standarts (que convecções vão ser utilizadas para garantir qualidade interna)
• Vão ser definidas métricas
Surge então o documento: quality assurance plan (plano de garantia de qualidade)
quality assurance plan: serve para indicar o que queremos fazer, quais os objetivos de qualidade, como vamos medir, que atividades vão ser realizadas
quality assessment report: serve para medir, para saber se conseguimos atingir, colocar as não conformidades (alturas em que não se seguiu o plano)
7. Capítulo das revisões:
“quanto mais tarde for encontrado o problema, por exemplo erro no código, mais caro fica a revisão”
Servem para ser descritas no quality assurance plan
Três tipos de revisões
• Inspeções:
o São reuniões
o Os vários inspetores vão analisar código ou documentos, à procura de defeitos
o Objetivo de encontrar defeitos
o Por exemplo ao software requirements specifications (SRS) e test plans
o Tem fases:
Planeamento: um documento (SRS e Vision and Scope e tudo o que estiver relacionado), o autor recolhe a informação e chama o moderador que organiza a reunião
Preparação: antes da reunião cada um dos inspetores (que são todos) vai ler, e toma nota do que for suspeito
Reunião: alguém vai ler o documento e vai explicando. Os outros fazem perguntas, e quem vai responder é quem lê. Se encontrarem defeitos o anotador toma notas. O autor está calado. Surge a discussão/debate.
Rework: o autor vai corrigir de acordo com as anotações
Follow-up: o moderador vai ver se as correções foram bem feitas.
[moderador (pode pedir pelas notas, decide o que é para anotar ou não), leitor (lê o documento), inspetor (são todos menos o leitor), anotador]
Surge o review report: com os defeitos, gravidade dos defeitos
O processo da inspecção:
Três tipos de revisões (continuação)
• Controlo documental (deskchecks):
o O autor distribui o que quer rever para que alguém faça revisões, para revisores
Três tipos de revisões (continuação)
• Reunião de orientação (walkthroughs):
o O autor chama os revisores, que leem o documento
o Os revisores leem o documento antes da reunião
o Mais informal
8. Os testes ao software
Testar um código pode ser só ler
Testar pode ser aplicar dados artificiais
Testar pode ser uma revisão manual do código
Testes não revelam a ausência de problemas
Teste revelam os problemas do código
Os software review: são análises estáticas, leitura do código
Os software testing: é uma analise dinâmica, não funcionais
São ambos complementares ( E )
Os testes:
• Testes unitários:
o Modulo individual, funções, método, objetos, classes..
o Procurar defeitos
o Escrevo um método e vou logo testar
• Testes de integração (integration test):
o Módulo está feito e vou juntar a outro módulo
o Ver como correm as coisas, integrando ambos
o E podem ser:
Integração incremental: Juntar os dois, aos poucos
Big bang : juntar tudo feito, por várias pessoas
• Teste de aceitação:
o Deve existir um plano de testes
o Quando a aplicação estiver terminada
Test plans (ATP, plano de testes):
• indica como vamos testar, deve ser feito aquando do SRS
• sumariar as atividades relacionados com os testes
• qual vai ser a sequência de testes
• deve ser feito logo no início quando ainda não existe código.
• Ajuda a focar na forma como vai ser escrito o código.
• Servem para verificar se todos os requisitos foram escritos.
Com a ajuda do SRS software requirements specification
Surge:
QAP quality assurance plan
ATP Acceptance Test Plan (descrição dos testes)
ATR Acceptance Test report
Os testes (continuação):
• Testes de usabilidade:
o Satisfação do participante com o produto
o Através de tarefas
o Pode ser quantitativa (nível de satisfação)
o Pode ser qualitativa
9. Estimativa
Esforço (pessoas-hora) VS Duração (hora)
No escalonamento devemos alocar recursos: as pessoas em cada uma das tarefas
Deve ser inscrita a informação do WBS (work breakdown structure)
Ter em conta o overhead
Saber a duração das tarefas
Identificar as dependências (entre tarefas)
Exemplo: finish-to-start. Uma revisão só começa depois do documento estar pronto
Diagrama de gantt, por exemplo:
Podem ser usados buffers no escalonamento
Os buffers são tempos mortos para resolver atrasos imprevistos
Project planning process: (estimate = escalonar)
10. Monitorização e controlo (Project Monitoring And Control)
Monitorizar, é saber se as tarefas estão a ser realizadas dentro do plano
Controlar, é controlar o âmbito, o calendário, reestimar
Controlar: dentro do prazo, se está dentro do orçamento, se estão a gastar ou não o orçamento, e se o trabalho está a ser seguido conforme o planeado
Métodos para controlar
• Burndown chart
o Projetos curtos
o Iteração de uma só vez
o Verificar todos os dias o trabalho que falta realizar
o Verde o real:
indica o que falta fazer,
linha verde abaixo, estamos adiantados, falta menos do que o previsto
linha verde acima, estamos atrasados, por exemplo tínhamos planeado/estimado 80 horas mas deviam ser 100 horas
o cor de laranja planeado
cor de laranja começa com o número máximo de horas que temos até esgotarmos e chegarmos a zero horas disponíveis, e data respetiva do âmbito do projeto
o este gráfico depende das estimativas
o não diz quanto trabalho foi feito
o serve para ajudar a prever quando vai terminar
o não funciona com tarefas muito grandes
• Earned Value Analysis (EVA)
o Permite avaliar o desempenho de uma equipa
o Relacionado com metodologias tradicionais
o Projetos grandes ou pequenos
o Comparar o custo, comparar o escalonamento e o âmbito
o Prever se o projeto consegue terminar a tempo
o Tudo o que é feito tem valor
o O valor é o esforço que se dedica a uma tarefa
o Budget cost of work (BCW)
Cada tarefa é estimada em ternos de esforço, ou custo
o Actual cost of work (ACW)
Custo para completar a tarefa
Quanto tempo demoramos para completar uma tarefa
o Planned Value (PV)
Somar as tarefas todas planeadas até agora
Exemplo: se tinha uma tarefa de 5 horas + tarefa de 3 horas para terminar hoje, o plano seria ter 8 horas de ganho, é este o valor planeado
o Earned value (EV)
É o que efetivamente ganhamos, por exemplo as tais 8 horas
o Actual cost (AC)
Quanto custou, o que efetivamente custou: por exemplo se para a tarefa de 5 hora demorados 10, e se para a tarefa de 3 horas demoramos 6 então custou 16 horas
Análise ao gráfico:
• Linha azul (BAC)
o é as horas que temos para gastar
o qual é o orçamento para todo o trabalho
• Linha vermelha (PV)
o o que estamos a planear
o qual é o valor estimado do trabalho planeado que vai ser executado
o é o valor ganho
• linha amarela (EV)
o à medida que as tarefas vão sendo cumpridas
o para cima do atual cost (da verde): trabalhamos para além do que estava previsto, se calhar terminamos mais tarefas, pois existe um valor ganho mais elevado do que o valor planeado. Quer dizer que está a correr bem
o qual é o valor estimado do trabalho planeado realmente concretizado
• linha verde (AC)
o é o tempo que realmente se gastou
o qual foi o custo que aconteceu
11. Gestão do risco
O plano de risco é sobre algo que pode correr mal
Antecipar os problemas e dessa forma evitá-los
Planear para minimizar ou anular os efeitos do risco no projeto:
• Risco sobre o projeto:
o Se ficarmos doentes
• Risco sobre o produto:
o A framework pode correr mal por algum motivo
• Risco do negócio:
o produto muito bom, mas não vamos conseguir vender
• Risco da tecnologia, das pessoas, organizacional, ferramentas, requisitos, estimativa
• A gestão do risco tem como processos:
o Identificação do risco
Quais são os riscos (de tecnologias, de pessoas, de organização, de requisitos, de estimativa)
Existe uma reunião com todos os que vão participar
Mas para saber o que é o risco, precisamos de saber PRIMEIRO o que é o sucesso do projeto (threshold of success) [smart]: com objetivos pequenos [short], mensuráveis [mensurable], alcançáveis (viável) [achivable], relevantes [relevant], e dentro do prazo [time bound]
Risk statment: O que é que hoje é verdade e porque é que isso me preocupa (condição-> então existe a possibilidade -> de existir uma consequência)
o Análise do risco
Qual a probabilidade dos riscos
Qua é o impacto do risco (risk impact): catastrófico(5), sério(3), tolerável(1)
Probabilidade do risco: baixa(1), moderada, alta. muito alta (5)
E criar prioridades através do resultado da: probabilidade * impacto
Ordenada do mais alto para o baixo
Os mais importantes podem ser SÓ os catastróficos
o Planeamento do risco (risk statment)
É a terceira fase
Definir o que fazer sobre o risco que foi identificado
Escolhemos lidar só com os mais altos por exemplo (mitigar estes) e colocar sobre observação os médios, e os outros ignorar
Para os riscos elevados:
• Plano de prevenção (avoidance strategies): o que fazer para baixar a preocupação? O impacto do risco? Para se tornar um menor risco.. baixa a probabilidade
• Plano de minimização (minimization strategies): baixar o impacto do risco acontecer [outro elemento da equipa para substituir alguém]
• Plano de contingência (contigency plans): planos para lidar com o problema [negociar com o cliente o âmbito da aplicação]
o Monitorizar o risco
Vamos monitorizar
Regularmente verificar se existem alterações no risco
12. Requisitos do software
Primeiro perceber quais são as necessidades, através de entrevistas, perceber as regras se for um jogo, perceber o que o cliente quer
Vai ter casos de uso
Vai ter os requisitos funcionais: o que é que a aplicação vai fazer, uma descrição clara, descrição que vai ter que estar completa
Vai ter os requisitos não funcionais: ao nível do comportamento (ser fácil [definir o que é, um utilizador tem que terminar a operação ao fim de xx tempo ], ser rápido [demorar menos de dois segundos])
Fazer UI Mockups:
O SRS (software requirements specification) tem os casos de uso, requisitos…
Scope: Vision and Scope
Requirements : SRS (software requirements specification)
Design:
13. kickoff meeting
Gestor do projeto planeou tudo com o cliente e agora surge a primeira reunião entre a equipa e o cliente
14. software version control
Testar um módulo de cada vez
• Disadvantages of a test suite
o It’s a lot of extra programming
o However, a good test framework can help quite a lot
o You don’t have time to do all that extra work
o False! Experiments repeatedly show that test suites reduce debugging
time more than the amount spent building the test suite
• Advantages of a test suite
o Reduces total number of bugs in delivered code
o Makes code much more maintainable and refactorable
16. team management
As fases da formação de uma equipa:
17. processo de software
Ciclo de vida de um processo de software
Em cascata
Mapear a sequência de passos para desenvolver projetos
O modelo de processo
Qual o critério para começar
Qual o critério para terminar
Respostas de exame 1920_normal
[1] é conveniente ou mesmo obrigatório ter a equipa de desenvolvimento definida antes de iniciar a escrita do documento vision and scope? Porquê?
Não está. O VanS serve para definir o que vai ser feito, haver um entendimento entre o cliente e a empresa. Qual a visão e qual o problema a ser resolvido e o âmbito.
Não é preciso haver cliente. Isto é apenas definir o que é para ser feito, não é de todo obrigatório/necessário, talvez conveniente.
Mas para fazer o plano (escalonamento) SDP? Sim pela quantidade, mas não para especificar onde colocar cada um.
[2] indique um processo que tenha extrema importância num projeto critico, e que não foi levado muito a sério no caso do projeto que realizou em GPS. Deveria este processo ter sido abordado de outra forma em GPS?
O processo de risco: os riscos não eram muito elevados. Não eram processos críticos.
Ou deveríamos ter tido mais atenção porque a experiência da equipa não conseguiu abranger o número de riscos.
Riscos de estimativas.
Lidar com testes unitários.
Obter uma formação em como trabalhar em kotlin. Aumentar o budget para a qualidade para obter a formação.
Controlo dos documentos
[3] porque é que numa estimativa realizada com planning poker, quando as cartas apresentadas pelos vários participantes diferem, não se faz simplesmente uma média e passa-se rapidamente à tarefa seguinte? Explique então como se deve fazer.
Tem um grande interesse as cartas extremas (altas e baixas)
A média não interessa para nada.
É importante a discussão.
As cartas têm a ver com as horas. Muitas cartas muitas diferentes horas para distribuir. Para haver consenso mais rápido.
Técnica baseada em opiniões de peritos.
[4] defina um atributo externo e outro interno de qualidade para a unidade curricular de GPS. Explique porque são atributos de qualidade e porque os considera externo ou interno.
Externo: (visto pelo cliente/stakeholder) a complexidade para os alunos, a taxa de aprovação
Interno: (interessa para a equipa) reutilização do código, reutilização dos slides para o próximo ano
[5] uma vez que os testes de aceitação verificam o cumprimento de todos os requisitos, para que serve realizar testes unitários? É possível, no final de todos os testes, afirmar que o software não tem defeitos?
Os testes unitários servem para testar por exemplo, módulos individualmente.
Testar se cumpre o objetivo
Os testes unitários são usados dados artificiais
Isto não garante de forma alguma que o software não tem defeitos. Nunca se consegue testar todas as possibilidades
Os testes de aceitação têm acesso mais limitado, não conseguem cobrir tudo.
[6] imagine que está preparar-se para uma entrevista de emprego e está risco de não ser admitido. Escreva a preocupação na forma de um risk statment e descreva formas de mitigar o risco, por meio de:
a) uma ação de prevenção (avoidance)
b) uma ação de minimização (minimization)
c) e contingência
um risk statment é: O que é que hoje é verdade e porque é que isso me preocupa (condição-> então existe a possibilidade -> de existir uma consequência)
plano de risco: serve para prevenir as coisas, antecipar
risk statment: cv fraco E a empresa é exigente, consequência: não gostam do meu CV logo posso não ser contratado
probabilidade de acontecer: alta (3) porque a empresa é difícil
prevenção: melhorar o CV, cartas de referência (tem que se baixar a probabilidade)
minimização: vou ter outras entrevistas em empresas menos exigentes (baixa o impacto)
contingência: pensar no que vou fazer, trabalhar como freelancer, ou vou tirar um curso (quando não se consegue fazer nada, decide já)
[7] Observe o gráfico de EVA – Earned Value Analysis seguinte e as situações em que a equipa se encontrava, considerando apenas os valores relativos entre PV – Planned Value, EV – Earned Value e AC -Actual Cost. Em qual dos dias registados parece ter ocorrido um pronto importante de viragem no projeto? Explique o que se parece ter-se passado.
BAC
• é as horas que temos para gastar
• qual é o orçamento para todo o trabalho
PV
• o que estamos a planear
• qual é o valor estimado do trabalho planeado que vai ser executado
• é o valor ganho
EV
• à medida que as tarefas vão sendo cumpridas
• para cima do atual cost (da verde): trabalhamos para além do que estava previsto, se calhar terminamos mais tarefas, pois existe um valor ganho mais elevado do que o valor planeado. Quer dizer que está a correr bem
• qual é o valor estimado do trabalho planeado realmente concretizado
AC
• é o tempo que realmente se gastou
• qual foi o custo que aconteceu
Entre 15 e 22: trabalharam menos, do que tinham planeado e a partir daqui atrasou-se
Entre 22 e 29: não conseguiram alcançar os objetivos dessa semana, tiveram que compensar
Dia 22: a partir daqui atrasaram-se e nunca mais conseguiram alcançar até ao final
Acabou terminar o projeto com esforço maior do que estava previsto
[8] Explique como fez no seu projeto de GPS para desenhar a linha de Planned Value no gráfico de Earned Value Analysis. Por que motivo esta linha deveria estar o mais linear possível?
PV
O mais linear possível para ter o ritmo de trabalho constante. O mesmo número de horas todas as semanas.
Dividir o trabalho em tarefas mais pequenas
Estimar cada uma das tarefas
Distribuir ao longo das semanas
E tentar aproximar o número de horas por semana para que fosse sempre constante
[9] Considere o seguinte processo de Desenvolvimento de Software:
O arquiteto desenha, a partir do Software Requirements Specification (SRS), o Software Architecture and Desigh (SAD). Este documento é revisto e aprovado pela equipa. Depois, com base nos documentos SRS, SAD e Plano de Projeto, cada programador codifica um requisito, escreve os testes, e executa-os. Depois de concluído o módulo resultante é integrado com os restantes módulos já anteriormente criados. Depois de pronto, passa para o próximo requisito, até que todos os requisitos estejam concluídos.
Desenhe um diagrama deste processo, à semelhança do que usou nas aulas, como por exemplo o que consta da figura abaixo. Deve escrever os pressupostos que considerou e que não constam da descrição.
Proposta de trabalho (em Gaia)
“SABER PORTO is NOW hiring the best:
– CHARACTER ARTISTS
– ENVIRONMENT ARTISTS
– TEXTURE ARTISTS
If you’re interested to work in Porto and develop AAA games, send your CV and PORTFOLIO to jobs@saber3d.pt.”
+infos(rede social): LINK
+infos(oficial): http://www.bigmoonstudios.com/
Humble Software Bundle: Maps Extravaganza Encore (uma campanha)
Esta é outra campanha da Humble mas o caso não é tanto para GameDev mas para jogos de tabuleiro. Tratam-se de várias ferramentas, softwares que permite criar mapas para jogos de tabuleiro. Interessante este tipo de programas, que eu não conhecia :) (também não tinha procurado). Da lista de prendas desta campanha consta:
Token Treasury: Monsters
Battle Maps Collection
Floorplan Collection
Sources Maps: Castles
Symbol Set 2: Fantasy Floorplans
Source Maps: Temples, Tombs and Catacombs
Tome of Ultimate Mapping
Perspectives 3
Campaign Cartographer 3+ One Year License
Campaign Cartographer 3+ Lifetime License
City Designer 3
Dungeon Designer 3
+infos(a campanha): LINK
Humble Game Dev Map & Level Creator Bundle, Humble software bundle (uma campanha)
Está a decorrer outra campanha no Humble com algumas coisas interessantes para acerca de GameDev, nomeadamente alguns programas para a construção de mapas e níveis de jogos. Da lista consta:
Egyptian Tileset
World Map Pixel Art Tileset
Super Pixel Dungeon
Fantasy Map
Night City Game Level Kit
Tropical Island 2d game Tileset
Desert Tileset
Fantasy Village
Game Level Map Set Kit
Fantasy Jungle Pixel Art Tileset
House Interiors Tileset Pack
City Street Tileset Pack
WiraWiri Game Level Map Builder
Misty Forest Ground Tiles
595 Medieval 2D Game Asset Pack
Platformer Game Tile Set 3
Simple RPG Tileset
Platformer Game Tile Set 1
Super Pixel Ice Cavern Tileset
Cartoon Platformer TilesetPack
Mega Factory Scene Creation Pack
Pixel Art Tileset Collection
Game Level Map – 9 Different Worlds
Underwater Tile Set
Isometric Forest
Wolfsong Tilesets
Top Down Tileset Interior
16 Jump Vertical Game Backgrounds
2D Isometric Starter Style Kit
The Dungeon Top Down Tileset
Game Level Map Pack Side Scrolling
Top Down Tileset Forest
Mega Castle & Dungeon Pack
Game Level Map Creator For Water Levels
Landscape Constructor Set
Woodlands Level Map Creator
Green Greens Forest Platformer Tileset
+infos(a campanha): LINK
Proposta de trabalho (em Gaia)
“SABER PORTO is hiring the best:
– CONCEPT ARTISTS
– GAME DESIGNERS
– VISUAL FX ARTISTS
If you’re interested to work in Porto and develop AAA games, send your CV and PORTFOLIO to jobs@saber3d.pt”
+infos(rede social): LINK
+infos(oficial): http://www.bigmoonstudios.com/
Proposta de trabalho (em Gaia)
SABER PORTO continues to grow its fantastic team in PORTUGAL! WE ARE HIRING:
– IT MANAGER
– HUMAN RESOURCES MANAGER
– ADMINISTRATIVE ASSISTANT
– ASSISTANT PRODUCER
– CINEMATICS & VIDEO EDITOR
– COMMUNITY MANAGER
If you’re interested, send your CV to jobs@saber3d.pt
+infos(rede social): LINK
+infos(oficial): http://www.bigmoonstudios.com/
em kotlin: uma recyclerView
ficheiro: MainActivity.kt
package pt.deis.estuda.estudoslistview2 import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } fun onRecyclerView(view: View) { val intent = Intent(this,RecyclerViewActivity::class.java) startActivity(intent) } }
ficheiro: activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.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" tools:context=".MainActivity"> <Button android:onClick="onRecyclerView" android:id="@+id/button" android:layout_width="0dp" android:layout_height="wrap_content" android:padding="16dp" android:text="recycler view" android:textSize="24sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
ficheiro: activity_recycler_view.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto" app:cardCornerRadius="6dp" app:cardElevation="6dp" android:layout_margin="6dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:textStyle="bold" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:id="@+id/recyclerTV1" android:textSize="24sp" android:text="Text 1" /> <TextView android:background="#f0f0f0" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:id="@+id/recyclerTV2" android:textSize="20sp" android:text="Text 2" /> <TextView android:background="#808080" android:textColor="#ffffff" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="right" android:id="@+id/recyclerTV3" android:textSize="16sp" android:text="Text 3" /> </LinearLayout> </androidx.cardview.widget.CardView>
ficheiro: activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.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" tools:context=".RecyclerViewActivity" android:padding="16dp"> <androidx.recyclerview.widget.RecyclerView android:padding="4dp" android:id="@+id/recyclerviewList" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
ficheiro: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pt.deis.estdua.arecyclerview"> <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.ARecyclerView"> <activity android:name=".RecyclerViewActivity" android:label="ListView" android:parentActivityName=".MainActivity" /> <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: RecyclerViewActivity.kt
package pt.deis.estuda.arecyclerview import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.StaggeredGridLayoutManager import kotlinx.android.synthetic.main.activity_recycler_view.* import kotlin.random.Random class RecyclerViewActivity : AppCompatActivity() { data class Dados(val str1:String,val str2 : String, val str3:String) val data = arrayListOf<Dados>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_recycler_view) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.subtitle = "Exemplo Recycler View" //sorterar numeros aleatoriamente repeat(Random.nextInt(10,20)) { val item = Dados("Titulo ${Random.nextInt(0,1000)}",getStr(50,400),getStr(5,20)) data.add(item) } //atribuir um layoutManager, um gestor para organizar os objectos, existem assim 3 tipos //LinearLayoutManager ou GridLayoutManager ou StaggeredGridLayoutManager //LinearLayoutManager.VERTICA - > lista com scroll na vertical //recyclerviewList.layoutManager = LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false) //recyclerviewList.layoutManager = GridLayoutManager(this,2,GridLayoutManager.VERTICAL,false) recyclerviewList.layoutManager = StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL) //MyRVAdapter derivado do RecyclerView.Adapter recyclerviewList.adapter = MyRVAdapter(data) } fun getStr(minc:Int,maxc: Int) : String { var str = "" val nrc = Random.nextInt(minc,maxc) repeat(nrc) { str += Random.nextInt(65,90).toChar() } return str } //é obrigatório o objecto ViewHolder, que representa cada um dos itens que vai ser visualizado class MyRVAdapter(val data : ArrayList<Dados>) : RecyclerView.Adapter<MyRVAdapter.MyViewHolder>() { class MyViewHolder(view : View) : RecyclerView.ViewHolder(view) { var tv1 : TextView = view.findViewById(R.id.recyclerTV1) var tv2 : TextView = view.findViewById(R.id.recyclerTV2) var tv3 : TextView = view.findViewById(R.id.recyclerTV3) //não é necessário.. e o update vai ser chamado pelo onBindViewHolder fun update(str1:String,str2:String,str3:String) { tv1.text = str1 tv2.text = str2 tv3.text = str3 } } //criar as vistas override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_item ,parent,false) //e é retornado no contexto do MyViewHolder return MyViewHolder(view) } //inflate de um layout override fun onBindViewHolder(holder: MyViewHolder, position: Int) { holder.update(data[position].str1,data[position].str2,data[position].str3) } //método para saber quantos elementos tem a lista override fun getItemCount(): Int = data.size } }
ficheiro: build.gralde (Module)
plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-android-extensions' // obrigatorio para aceder aos diferentes layouts sem usar o findId } ...
em kotlin: uma ListView XML array adapter personalizada v4 (destaque)
ficheiro: MainActivity.kt
package pt.deis.estuda.estudoslistview2 import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View const val TAG = "ListView" class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } fun onList4View(view: View) { val intent = Intent(this,ListView4Activity::class.java) startActivity(intent) } }
ficheiro: activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.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" tools:context=".MainActivity"> <Button android:onClick="onList4View" android:id="@+id/button" android:layout_width="0dp" android:layout_height="wrap_content" android:padding="16dp" android:text="ListView XML array adapter personalizado v4" android:textSize="24sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
ficheiro: activity_list4_view.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=".ListView4Activity"> <ListView android:id="@+id/quartaListView" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>
ficheiro: arrays.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="dados_str"> <item>Portugal</item> <item>Espanha</item> <item>Franca</item> <item>Italia</item> <item>Alemanha</item> <item>Belgica</item> <item>Holanda</item> <item>Austria</item> <item>Luxemburgo</item> <item>Suecia</item> <item>Dinamarca</item> <item>Irlanda</item> <item>Croacia</item> <item>Eslovenia</item> <item>Eslovaquia</item> <item>Estonia</item> <item>Polonia</item> <item>Grecia</item> <item>Finlandia</item> <item>Chipre</item> <item>Malta</item> <item>Bulgaria</item> <item>Hungria</item> <item>Romenia</item> <item>Rep. Checa</item> <item>Letonia</item> <item>Lituania</item> </string-array> </resources>
ficheiro: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pt.deis.estuda.estudoslistview4"> <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.Estudoslistview4"> <activity android:name=".ListView4Activity" android:label="ListView" android:parentActivityName=".MainActivity" /> <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: ListView4Activity.kt
package pt.deis.estuda.estudolistview4 import android.graphics.Color import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.BaseAdapter import android.widget.ImageView import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import kotlinx.android.synthetic.main.activity_list4_view.* import kotlin.random.Random class ListView4Activity : AppCompatActivity() { data class Pais (val nome : String, var habitantes:Int) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_list4_view) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.subtitle = "Exemplo 4" val paises = resources.getStringArray(R.array.dados_str) paises.sort() val data = arrayListOf<Pais>() for(p in paises) { val item = Pais(p, Random.nextInt(100000,99000000)) data.add(item) } val adapter = MyAdapter(data) quartaListView.adapter = adapter quartaListView.setOnItemClickListener() { parent, view, pos, id -> Log.i(TAG, "Item: $pos $id") } } //adapter personalizado derivado do BaseAdapter class MyAdapter(val data : ArrayList<Pais>) : BaseAdapter() { val imgs = arrayOf( android.R.drawable.ic_menu_agenda, android.R.drawable.ic_menu_camera, android.R.drawable.ic_menu_call, android.R.drawable.ic_menu_compass ) //sendo MyAdapater abstracta tenho que implementar os 4 próximos métodos //método devolve quantos items (usar do construtor do data) override fun getCount(): Int = data.size //método retorna o elemento de dados que está numa posição override fun getItem(position: Int): Any { return data[position] } //método para uma posição indica override fun getItemId(position: Int): Long = position.toLong() //metodo para retornar uma view, é um layout com tudo preenchido com o que queremos //o elemento neste caso é o position override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { //fazer o inflate do listview_item val view = LayoutInflater.from(parent!!.context).inflate(R.layout.listview4_item, parent, false) //para preencher os campos view.findViewById<TextView>(R.id.tv1).text = data[position].nome //usar o tv1 e tv2 para inserir os dados view.findViewById<TextView>(R.id.tv2).apply { text = data[position].habitantes.toString() //filtro dos dados dependendo das cores if (data[position].habitantes > 80000000) { setBackgroundColor(Color.rgb(128, 0, 0)) setTextColor(Color.WHITE) } //para reduzir o numero de abitantes quanto se dá um toque setOnClickListener { data[position].habitantes = (data[position].habitantes * 0.5).toInt() this@MyAdapter.notifyDataSetChanged() } //quando se faz o long click reponho o numero de habitantes setOnLongClickListener { data[position].habitantes = 99_000_000 this@MyAdapter.notifyDataSetChanged() true } } //associo uma imagem, sorteadas view.findViewById<ImageView>(R.id.ivImg) .setImageResource(imgs[Random.nextInt(imgs.size)]) return view } } }
ficheiro: listview4_item.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="4dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="4dp" android:background="#800000"> <ImageView android:id="@+id/ivImg" android:src="@android:drawable/ic_dialog_map" android:layout_width="32dp" android:layout_height="32dp" /> <TextView android:textStyle="bold" android:padding="4dp" android:textSize="20sp" android:text="text1" android:layout_width="0dp" android:layout_weight="2" android:layout_height="wrap_content" android:id="@+id/tv1" android:textColor="#000000" android:background="#f0d080"/> <TextView android:padding="4dp" android:textSize="20sp" android:gravity="right" android:text="text2" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:id="@+id/tv2" android:background="#80d0f0"/> </LinearLayout> </FrameLayout>
ficheiro: build.gralde (Module)
plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-android-extensions' // obrigatorio para aceder aos diferentes layouts sem usar o findId } ...
em kotlin: uma ListView XML array adapter personalizada v2 (numeros)
ficheiro: MainActivity.kt
package pt.deis.estuda.estudoslistview2 import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View const val TAG = "ListView" class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } fun onList2View(view: View) { val intent = Intent(this,ListView3Activity::class.java) startActivity(intent) } }
ficheiro: activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.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" tools:context=".MainActivity"> <Button android:onClick="onList2View" android:id="@+id/button" android:layout_width="0dp" android:layout_height="wrap_content" android:padding="16dp" android:text="ListView XML array adapter personalizado v2" android:textSize="24sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
ficheiro: activity_list3_view.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=".ListView3Activity"> <ListView android:id="@+id/terceiraListView" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>
ficheiro: arrays.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="dados_str"> <item>Portugal</item> <item>Espanha</item> <item>Franca</item> <item>Italia</item> <item>Alemanha</item> <item>Belgica</item> <item>Holanda</item> <item>Austria</item> <item>Luxemburgo</item> <item>Suecia</item> <item>Dinamarca</item> <item>Irlanda</item> <item>Croacia</item> <item>Eslovenia</item> <item>Eslovaquia</item> <item>Estonia</item> <item>Polonia</item> <item>Grecia</item> <item>Finlandia</item> <item>Chipre</item> <item>Malta</item> <item>Bulgaria</item> <item>Hungria</item> <item>Romenia</item> <item>Rep. Checa</item> <item>Letonia</item> <item>Lituania</item> </string-array> </resources>
ficheiro: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pt.deis.estuda.estudoslistview3"> <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.Estudoslistview3"> <activity android:name=".ListView3Activity" android:label="ListView" android:parentActivityName=".MainActivity" /> <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: ListView3Activity.kt
package pt.deis.estuda.estudolistview3 import android.os.Bundle import android.util.Log import android.widget.SimpleAdapter import androidx.appcompat.app.AppCompatActivity import kotlinx.android.synthetic.main.activity_list3_view.* import kotlin.random.Random class ListView3Activity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_list3_view) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.subtitle = "Exemplo 3" val paises = resources.getStringArray(R.array.dados_str) paises.sort() /* val data1 = mapOf("valor1" to 10, "valor2" to 20) val data2 = mapOf("valor1" to 11, "valor2" to 21) val data = mutableListOf(data1,data2) */ val data = mutableListOf<Map<String,Any>>() //Any() para ser qualquer tipo de dados //val i = Random.nextInt(10,20) for(p in paises) { val item = mapOf<String,Any>("valor1" to p, "valor2" to Random.nextInt(100000,99000000), "imagem" to android.R.drawable.ic_menu_compass) data.add(item) } val adapter = SimpleAdapter(this, data, R.layout.listview3_item, arrayOf("valor1","valor2","imagem"), intArrayOf(R.id.tv1, R.id.tv2, R.id.ivImg) ) //tv1 (recebe valor1), tv2 (recebe valor2)... dados das linhas terceiraListView.adapter = adapter terceiraListView.setOnItemClickListener() { parent, view, pos, id -> Log.i(TAG, "Item: $pos $id") } } }
ficheiro: listview3_item.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="4dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="4dp" android:background="#800000"> <ImageView android:id="@+id/ivImg" android:src="@android:drawable/ic_dialog_map" android:layout_width="32dp" android:layout_height="32dp" /> <TextView android:textStyle="bold" android:padding="4dp" android:textSize="20sp" android:text="text1" android:layout_width="0dp" android:layout_weight="2" android:layout_height="wrap_content" android:id="@+id/tv1" android:textColor="#000000" android:background="#f0d080"/> <TextView android:padding="4dp" android:textSize="20sp" android:gravity="right" android:text="text2" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:id="@+id/tv2" android:background="#80d0f0"/> </LinearLayout> </FrameLayout>
ficheiro: build.gralde (Module)
plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-android-extensions' // obrigatorio para aceder aos diferentes layouts sem usar o findId } ...
em kotlin: uma ListView XML array adapter personalizada
ficheiro: MainActivity.kt
package pt.deis.estuda.estudoslistview2 import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View const val TAG = "ListView" class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } fun onList2View(view: View) { val intent = Intent(this,ListView2Activity::class.java) startActivity(intent) } }
ficheiro: activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.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" tools:context=".MainActivity"> <Button android:onClick="onList2View" android:id="@+id/button" android:layout_width="0dp" android:layout_height="wrap_content" android:padding="16dp" android:text="ListView XML array adapter personalizado" android:textSize="24sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
ficheiro: activity_list2_view.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=".ListView2Activity"> <ListView android:id="@+id/segundaListView" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>
ficheiro: arrays.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="dados_str"> <item>Portugal</item> <item>Espanha</item> <item>Franca</item> <item>Italia</item> <item>Alemanha</item> <item>Belgica</item> <item>Holanda</item> <item>Austria</item> <item>Luxemburgo</item> <item>Suecia</item> <item>Dinamarca</item> <item>Irlanda</item> <item>Croacia</item> <item>Eslovenia</item> <item>Eslovaquia</item> <item>Estonia</item> <item>Polonia</item> <item>Grecia</item> <item>Finlandia</item> <item>Chipre</item> <item>Malta</item> <item>Bulgaria</item> <item>Hungria</item> <item>Romenia</item> <item>Rep. Checa</item> <item>Letonia</item> <item>Lituania</item> </string-array> </resources>
ficheiro: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pt.deis.estuda.estudoslistview2"> <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.Estudoslistview2"> <activity android:name=".ListView2Activity" android:label="ListView" android:parentActivityName=".MainActivity" /> <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: ListView2Activity.kt
package pt.deis.estuda.estudolistview import android.os.Bundle import android.util.Log import android.widget.ArrayAdapter import androidx.appcompat.app.AppCompatActivity import kotlinx.android.synthetic.main.activity_list2_view.* class ListView2Activity : AppCompatActivity() { var flag = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_list2_view) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.subtitle = "Exemplo 2" val paises = resources.getStringArray(R.array.dados_str) paises.sort() //id do elemento das strings ->,R.id.tv1 val adapter1 = ArrayAdapter<String>(this,R.layout.listview2_item,R.id.tv1,paises) //a segunda caixa de texto -> R.id.tv2 val adapter2 = ArrayAdapter<String>(this,R.layout.listview2_item,R.id.tv2,paises) segundaListView.adapter = adapter1 segundaListView.setOnItemClickListener() { parent, view, pos, id -> Log.i(TAG, "Item: $pos $id") //adapter1.notifyDataSetChanged() //serve para forçar o actualizar os dados segundaListView.adapter = if (flag) adapter1 else adapter2 flag = !flag } } }
ficheiro: listview2_item.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="4dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="4dp" android:background="#800000"> <ImageView android:id="@+id/ivImg" android:src="@android:drawable/ic_dialog_map" android:layout_width="32dp" android:layout_height="32dp" /> <TextView android:textStyle="bold" android:padding="4dp" android:textSize="20sp" android:text="text1" android:layout_width="0dp" android:layout_weight="2" android:layout_height="wrap_content" android:id="@+id/tv1" android:textColor="#000000" android:background="#f0d080"/> <TextView android:padding="4dp" android:textSize="20sp" android:gravity="right" android:text="text2" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:id="@+id/tv2" android:background="#80d0f0"/> </LinearLayout> </FrameLayout>
ficheiro: build.gralde (Module)
plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-android-extensions' // obrigatorio para aceder aos diferentes layouts sem usar o findId } ...
em kotlin: uma ListView XML array adapter
ficheiro: MainActivity.kt
package pt.deis.estuda.estudolistview import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View const val TAG = "ListView" class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } //nvoas actividades para fun onListView1(view: View) { val intent = Intent(this,ListView1Activity::class.java) startActivity(intent) } }
ficheiro: activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.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:background="#f0f0f0" android:padding="16dp" tools:context=".MainActivity"> <Button android:onClick="onListView1" android:id="@+id/button" android:layout_width="0dp" android:layout_height="wrap_content" android:padding="16dp" android:text="ListView XML array adapter" android:textSize="24sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
ficheiro: activity_list_view.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=".ListView1Activity"> <ListView android:id="@+id/primeiraListView" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>
ficheiro: arrays.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="dados_str"> <item>Portugal</item> <item>Espanha</item> <item>Franca</item> <item>Italia</item> <item>Alemanha</item> <item>Belgica</item> <item>Holanda</item> <item>Austria</item> <item>Luxemburgo</item> <item>Suecia</item> <item>Dinamarca</item> <item>Irlanda</item> <item>Croacia</item> <item>Eslovenia</item> <item>Eslovaquia</item> <item>Estonia</item> <item>Polonia</item> <item>Grecia</item> <item>Finlandia</item> <item>Chipre</item> <item>Malta</item> <item>Bulgaria</item> <item>Hungria</item> <item>Romenia</item> <item>Rep. Checa</item> <item>Letonia</item> <item>Lituania</item> </string-array> </resources>
ficheiro: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pt.deis.estuda.estudolistview"> <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.Estudolistview"> <activity android:name=".ListView1Activity" android:label="ListView" android:parentActivityName=".MainActivity" /> <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: ListView1Activity.kt
package pt.deis.estuda.estudolistview import android.os.Bundle import android.util.Log import android.widget.ArrayAdapter import androidx.appcompat.app.AppCompatActivity import kotlinx.android.synthetic.main.activity_list_view.* class ListView1Activity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_list_view) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.subtitle = "Exemplo XML array list" //uma lista não ordenada em arrays.xml val paises = resources.getStringArray(R.array.dados_str) paises.sort() //usar um adapter para mostrar a listview //contexto (actividades), layout que só tem uma textview, a fonte dos dados (array das strings) val adapter = ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,paises) primeiraListView.adapter = adapter primeiraListView.setOnItemClickListener() { parent, view, pos, id -> Log.i(TAG, "Item: $pos $id") } } }
ficheiro: build.gralde (Module)
plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-android-extensions' // obrigatorio para aceder aos diferentes layouts sem usar o findId } ...
GameDevCamp em Portugal, 7ª edição.
+infos(oficial): https://2020.gamedevcamp.org/
Proposta de trabalho
Na Fun Punch Games estamos à procura de um narrative designer/writer para um dos nossos projectos, em regime freelance. Alguém que perceba as possibilidade e limitações da escrita para videojogos, a sua proximidade com as mecânicas de jogo e os seus sistemas, os personagens, sítios, temas e situações associadas. Que saiba como “traduzir” mecânicas para histórias e vice-versa. Alguém que goste de trabalhar em equipa, de discutir ideias e implementação, que queira crescer!
Requisitos:
– Portfolio
– Experiência prévia
– Excelente nível de inglês: muito importante!
Enviem as vossas candidaturas para jobs@funpunchgames.com com o assunto “Narrative Designer”
+infos(oficial): https://funpunchgames.com/
em kotlin: uma calculadora
ficheiro: MainActivity.kt
package pt.deis.estuda.calculadora import android.graphics.Color import android.icu.number.NumberFormatter import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View import android.widget.Button import android.widget.TextView class MainActivity : AppCompatActivity() { lateinit var tvDisplay: TextView //variave para ligar a maquina var strDisplay = "0.0" var novoNumero = true //para tratar do % val numeroCorente : Double //caso dê erro converter fica com 0.0 //o então escrever error get()=strDisplay.toDoubleOrNull() ?: 0.0 //variavel para lidar com a operação //v1 /* enum class Ops{ NONE, ADD, SUB, MUL, DIv } var op = Ops.NONE */ //v2 //id da operação var operador = 0 //alterar a cor do operador set(value){ if(field !=0 ){ findViewById<Button>(field).setTextColor(Color.BLACK) } if(value !=0){ findViewById<Button>(value).setTextColor(Color.GREEN) } field =value } var numeroAntigo = 0.0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) tvDisplay = findViewById(R.id.tvDisplay) updateDisplay() //para mostrar o 0.0 } fun updateDisplay(){ tvDisplay.text = strDisplay } fun onDigit(view: View) { ///ir buscar o texto do botão e adicionar à caixa val btn = view as Button //para resolver a situaçãod e ser a primeira vez if(novoNumero) { strDisplay = btn.text.toString() } else { strDisplay += btn.text //concatenar ao que lá está } updateDisplay() //para que efectivamente faça a concatenação novoNumero = false } fun onPonto(view: View) { //só pode exisir um ponto no numero if(novoNumero) { strDisplay = "0." }else{ //adicionar o ponto, mas testar se já existe if(strDisplay.contains('.')) return strDisplay += "." } updateDisplay() novoNumero = false } fun onAC(view: View) { strDisplay = "0.0" novoNumero = true updateDisplay() } fun onPM(view: View) { //o sinal do mais menos if(strDisplay[0] == '-'){ strDisplay = strDisplay.substring(1) }else{ //ou strDisplay = "-"+strDisplay //v1 strDisplay = "-$strDisplay" //v2 } updateDisplay() } fun onPer(view: View) { //dividir o valor que existe por 100 strDisplay = "" + numeroCorente / 100.0 updateDisplay() //talvez não usar o true //true: não permite acrescentar mais caracteres novoNumero = true } //versão individual do onOper //v1 fun onMais(view: View) {} fun onSub(view: View) {} fun onDiv(view: View) {} fun onMultiplica(view: View) {} //v2 fun onOper(view : View){ //manter as operações if(operador != 0 && !novoNumero){ onIgual(view) } numeroAntigo = numeroCorente novoNumero = true operador = view.id } fun onIgual(view: View) { var calculo = 0.0 when(operador){ R.id.bntMais -> calculo = numeroCorente + numeroAntigo R.id.btnMul -> calculo = numeroCorente * numeroAntigo R.id.bntSub -> calculo = numeroAntigo - numeroCorente R.id.bntDiv -> calculo = if(numeroCorente != 0.0) numeroAntigo / numeroCorente else 0.0 //caso não tenha operador para fazer else -> return } //actualizar de seguida o display strDisplay = "$calculo" novoNumero = true operador = 0 //manter o numero numeroAntigo = calculo updateDisplay() } }
ficheiro: themes.xml
<resources xmlns:tools="http://schemas.android.com/tools"> <!-- Base application theme. --> <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%20name%3D%22Theme.Calculadora%22%20parent%3D%22Theme.AppCompat.Light.DarkActionBar%22%3E%0A%20%20%20%20%20%20%20%20%3C!--%20alterar%20o%20tema%20aqui.%20--%3E%0A%0A%20%20%20%20%20%20%20%20%3C!--%20cor%20preta%20default%20--%3E%0A%20%20%20%20%20%20%20%20%3Citem%20name%3D%22android%3AtextColor%22%3E%23000000%3C%2Fitem%3E%0A%0A%20%20%20%20%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<style>" title="<style>" /> </resources>
ficheiro: activity_main.xml
<?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" tools:context=".MainActivity" android:orientation="vertical" android:background="#c0c0c0" android:padding="16dp" > <TextView android:id="@+id/tvDisplay" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="0.0" android:gravity="right" android:textSize="24sp" android:padding="16dp" android:background="#ffffc0" android:layout_marginBottom="20dp" /> <!-- android:layout_height="0dp" //distribuir o espaço da mesma forma (é o peso) android:layout_weight="1" //por termos peso a altura tem que ter 0dp --> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:orientation="horizontal"> <!-- android:id="@+id/bntAC" //atribuir sempre um id android:layout_weight="1" //distribuir o espaço da mesma forma (é o peso) android:layout_width="0dp" //por termos peso a largura tem que ter 0dp --> <Button android:id="@+id/bntAC" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:text="AC" android:textSize="20sp" android:onClick="onAC" /> <Button android:id="@+id/bntPM" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:text="+/-" android:textSize="20sp" android:onClick="onPM" /> <Button android:id="@+id/bntPerc" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:text="%" android:textSize="20sp" android:onClick="onPer" /> <Button android:id="@+id/bntDiv" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:text="/" android:textSize="20sp" android:onClick="onOper" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:orientation="horizontal"> <!-- android:id="@+id/bntAC" //atribuir sempre um id android:layout_weight="1" //distribuir o espaço da mesma forma (é o peso) android:layout_width="0dp" //por termos peso a largura tem que ter 0dp android:onClick="onDigit" é uma funça --> <Button android:id="@+id/bnt7" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:onClick="onDigit" android:text="7" android:textSize="20sp" /> <Button android:id="@+id/bnt8" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:onClick="onDigit" android:text="8" android:textSize="20sp" /> <Button android:id="@+id/bnt9" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:onClick="onDigit" android:text="9" android:textSize="20sp" /> <Button android:id="@+id/btnMul" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:text="*" android:textSize="20sp" android:onClick="onOper" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:orientation="horizontal"> <!-- android:id="@+id/bntAC" //atribuir sempre um id android:layout_weight="1" //distribuir o espaço da mesma forma (é o peso) android:layout_width="0dp" //por termos peso a largura tem que ter 0dp --> <Button android:id="@+id/bnt4" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:onClick="onDigit" android:text="4" android:textSize="20sp" /> <Button android:id="@+id/bnt5" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:onClick="onDigit" android:text="5" android:textSize="20sp" /> <Button android:id="@+id/bnt6" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:onClick="onDigit" android:text="6" android:textSize="20sp" /> <Button android:id="@+id/bntSub" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:text="-" android:textSize="20sp" android:onClick="onOper" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:orientation="horizontal"> <!-- android:id="@+id/bntAC" //atribuir sempre um id android:layout_weight="1" //distribuir o espaço da mesma forma (é o peso) android:layout_width="0dp" //por termos peso a largura tem que ter 0dp --> <Button android:id="@+id/bnt1" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:onClick="onDigit" android:text="1" android:textSize="20sp" /> <Button android:id="@+id/bnt2" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:onClick="onDigit" android:text="2" android:textSize="20sp" /> <Button android:id="@+id/bnt3" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:onClick="onDigit" android:text="3" android:textSize="20sp" /> <Button android:id="@+id/bntMais" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:text="+" android:textSize="20sp" android:onClick="onOper" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:orientation="horizontal"> <!-- android:id="@+id/bntAC" //atribuir sempre um id android:layout_weight="1" //distribuir o espaço da mesma forma (é o peso) android:layout_width="0dp" //por termos peso a largura tem que ter 0dp --> <Button android:id="@+id/bnt0" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="2" android:onClick="onDigit" android:text="0" android:textSize="20sp" /> <Button android:id="@+id/bntPonto" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:text="." android:textSize="20sp" android:onClick="onPonto" /> <Button android:id="@+id/bntIgual" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:text="=" android:textSize="20sp" android:onClick="onIgual" /> </LinearLayout> </LinearLayout>
Laboratório de Jogos do IST: Alumni Meeting 2020
Cheguei a tempo de assistir à palestras do LabJogos, e fiquei a saber de mais um tuga que anda lá por fora a desenvolver o seu percurso no mundo dos videojogos, de nome João Oliveira que trabalha na Ubisoft.
+infos(a conversa): https://fb.watch/1Vk72BUeZN/
Dice Command
Esteve a decorrer uma campanha acerca deste jogo de tabuleiro. Uma vez mais achei interessante o tema… mas achei caro com os portes de envio! Mas fica o registo da sua existência :)
+infos(oficial): https://www.ejectedplanet.com/
+infos(campanha): LINK
Sea of Roses, um videojogo
Sea of Roses
Desenvolvido por: Ariana Parrilha (FBAUL), Andreia Santos Batista (FBAUL) & André Fidalgo Silva (IST)
Humble Game Dev STEM, Humble software bundle (uma campanha)
Está a decorrer mais uma campanha no Humble com algumas coisas interessantes para acerca de GameDev, nomeadamente alguns programas para a construção da documentação e alguns extras para uso com o software Game Creator. Da lista consta:
SoftWeir Game Design Documentation Builder
SoftWeir Project Management Documentation
SoftWeir World Design Worksheet
SoftWeir Map & Dungeon Design Worksheet
SoftWeir User-Interface Design Worksheet
SoftWeir Character & Animation Design Worksheet
SoftWeir Story & Storyboard Design Worksheet
SoftWeir Script & Logic Design Worksheet
SoftWeir Item Design Worksheet
SoftWeir Weapon & Armour Design Worksheet
001 Game Creator
001 Basics E-Book + E-Learning Home Assignments
001 Resource E-Book + E-Learning Home Assignments
DLC 001 Game Creator – Point & Click Adventure Kit
DLC 001 Game Creator – Dragons Den Resource Pack
DLC 001 Game Creator – Retro Fantasy Music Pack Volume 1
DLC 001 Game Creator – Sound Effects Pack Volume 1
DLC 001 Game Creator – Enhanced RPG Kit
DLC 001 Game Creator – 3D FPS / Survival Horror Kit
DLC 001 Game Creator – MMORPG Kit
DLC 001 Game Creator – Visual Novel Kit
+infos(campanha): LINK