Oldalak

2012. december 15., szombat

Élő háttérkép (live wallpaper demo) létrehozása

Élő háttérkép alkalmazás létrehozásának lépései:



    
    
    
        
         
            
                
            
            
        
        
    



Az alkalmazás manifest állományában az élő háttérkép alkalmazáshoz meg kell adni az élő háttérkép jogosultságot android:permission="android.permission.BIND_WALLPAPER", illetve a market felé az élő háttérkép alkalmazást jelezni kell
 
node -ban. A service node-ban az android:name=".OwnWallpaper" rész tartalmazza annak az osztálynak a nevét, ami megvalósítja az élő háttérkép osztályt. Az élő háttérkép alkalmazáshoz létre kell hozni egy XML állományt is, aminek az alábbi módon kell kinéznie (erre a manifest állományban is hivatkozni kell android:resource="@xml/own"):

Az own.xml állományt a /res/xml/own.xml útvonalon kell létrehozni




















Az OwnWallpaper osztály pedig az alábbiak szerint kell megcsinálni:

package hu.globomax.wallpaperdemo;

import java.util.Date;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.service.wallpaper.WallpaperService;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

public class OwnWallpaper extends WallpaperService {

 @Override
 public Engine onCreateEngine() {
  
  return new CubeEngine();
 }

 class CubeEngine extends Engine {
  private final Paint mPaint = new Paint();
  private float mTouchX = 0;
  private float mTouchY = 0;
  
  CubeEngine() {
   final Paint paint = mPaint;
   paint.setColor(0xffffffff);
   paint.setTextSize(25);
  }

  @Override
  public void onCreate(SurfaceHolder surfaceHolder) {
   super.onCreate(surfaceHolder);
   // Érintés eseményre kezelés jelzése
   setTouchEventsEnabled(true);
  }

  @Override
  public void onTouchEvent(MotionEvent event) {
   super.onTouchEvent(event);

   if (event.getAction() == MotionEvent.ACTION_UP) {
    mTouchX = -1;
    mTouchY = -1;
   } else {
    mTouchX = event.getX();
    mTouchY = event.getY();
   }
   drawFrame();
  }

  @SuppressWarnings("deprecation")
  void drawFrame() {
   final SurfaceHolder holder = getSurfaceHolder();
   Canvas c = null;
   try {
    c = holder.lockCanvas();
    if (c != null) {
     c.save();
     c.drawColor(0xff000000);
     c.drawText(new Date(System.currentTimeMillis())
       .toLocaleString(), mTouchX, mTouchY, mPaint);
     c.restore();
    }
   } finally {
    if (c != null)
     holder.unlockCanvasAndPost(c);
   }
  }
 }
}

Az így elkészült alkalmazás pedig az alábbiak szerint viselkedik:


Fontos eseménykezelő metódusok:
onCreateEngine()

  • WallpaperService.Engine létrehozása
A létrehozott Engine fontosabb eseménykezelő metódusai:
onVisibilityChanged():
  • ha háttérbe kerül az alkalmazás
onOffsetChanged()
  • a home screen-ek közötti váltáskor üt be
onTouchEvent():
  • érintéskor bekövetkező eseménykezelő
onCommand():
  • más alkalmazás által küldött üzenetek érkezésekor üt be

Saját téma használata az alkalmazásban

Saját téma készítése az android:Theme -ből származtatva
/res/values/styles.xml -t ki kell egészíteni az alábbiak szerint:

    
Egyedi színeket definiáló XML-t létre kell hozni, ami leírja a mygreen színt. A /res/values/ könyvtárban létre kell hozni egy újabb XML állományt tetszőleges néven. Ennek az XML-nek az alábbi leírást adhatjuk:



    #11ff11


Bár ezt a /res/values/styles.xml -ben is lehetne tárolni, de így lehet áttekinthetőbb a kód talán.

Következő lépésben az alkalmazásunk manifest állományában az activity -nek "meg kell mondani", hogy az általunk definiált témát használja a program.



    

    
        
            
                

                
            
        
    



A fenti XML-ben az activity node-ban az android:theme="@style/CustomTheme" attribútumban lehet megadni, hogy az activity komponens az általunk definiált témát használja.

Az alkalmazás pedig az alábbi módon fog megjelenni:

Saját stílus használata a UI elemeken

Az egyéni stílusok használatát a UI elemeken az alábbi módon tehetjük meg. Első lépésben a projekthez tartozó stílusokat leíró XML-t kell megnyitni.


















Stílus állomány: res/values/styles.xml

Az XML-be az alábbi kódrészletet kell beilleszteni:


Második lépésben a UI-t leíró XML-ben a felületen megjelenő elemekhez hozzá kell adni az egyedileg definiált stílust.
/res/layout/activity_layout.xml


A stílust a style attribútumban lehet megadni: style="@style/ExampleStyle"

A programban pedig az alábbi módon fog megjelenni a stílus:

Almenü kezelés megvalósítása XML erőforrásban

Az almenü kezelés XML erőforrásban történő megvalósítása a következő képen történik: A project explorer-ben az erőforrások közül az activity-hez tartozó menü leíró XML-t kell megnyitni.


















Az almenü XML megvalósítását az alábbi példa szemlélteti:


    
    
    
        
            
            
        
    


A menü kezelésre pedig az alábbi java kód szükséges (a menüpontok kiválasztásra csak egy egyszerű Toast üzenetet jelenít meg):
package hu.globomax.submenudemo;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

public class MainActivity extends Activity {

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

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.activity_main, menu);
  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  switch (item.getItemId()) {
  case R.id.item1:
   Toast.makeText(this, "Első menüpont kiválasztva", Toast.LENGTH_LONG).show();
   break;
  case R.id.item2:
   Toast.makeText(this, "Második menüpont kiválasztva", Toast.LENGTH_LONG).show();
   break;
  case R.id.submenu:
   Toast.makeText(this, "Almenüpont kiválasztva", Toast.LENGTH_LONG).show();
   break;
  case R.id.submenu_item1:
   Toast.makeText(this, "Almenüpont 1 kiválasztva", Toast.LENGTH_LONG).show();
   break;
  case R.id.submenu_item2:
   Toast.makeText(this, "Almenüpont 2 kiválasztva", Toast.LENGTH_LONG).show();
   break;
  default:
   break;
  }
  return true;
 } 
}
Az így elkészített almenü a telefonon az alábbi módon fog megjelenni:

Alert dialógus ablak

Alert dialógus ablak készítéséhez az alábbi kódrészletet kell használni:
private void showAlertMessage(final String aMessage) {
 AlertDialog.Builder alertbox = new AlertDialog.Builder(this);
 alertbox.setMessage(aMessage);
 alertbox.setNeutralButton("Ok", new DialogInterface.OnClickListener() {
  public void onClick(DialogInterface arg0, int arg1) {
   Toast.makeText(MainActivity.this, "Hello", Toast.LENGTH_LONG).show();
   }
 });
 alertbox.show();
}
A fenti metódust meghívva az Alert dialógus ablak az alábbi módon fog megjelenni a telefonon:

2012. december 1., szombat

Popup demo

Az alábbi példában Popup ablak létrehozása és használata látható:
A kész program az alábbi módon jelenik majd meg:

A fő Activity komponens UI felülete az alábbi legyen:



    
A res/layout könyvtárba létre kell hozni egy új XML állományt, ami a Popup ablak felületét fogja leírni. Ennek a forrása az alábbi legyen:


      
      
      

Ezután a fő Activity osztályban a gomb esemény kezelőjében meg kell valósítani a PopUp ablak megjelenítését. Ennek a kódja az alábbi lesz:

package hu.myexamples.popupdemo;

import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.PopupWindow;

public class MainActivity extends Activity {

 private Button btnPopup;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  btnPopup = (Button) findViewById(R.id.btnPopup);
 }

 public void onClick(View v){
  if (v.getId() == R.id.btnPopup) {
   LayoutInflater layoutInflater = (LayoutInflater) getBaseContext().getSystemService(LAYOUT_INFLATER_SERVICE);
   View popupView = layoutInflater.inflate(R.layout.popup, null);
   final PopupWindow popupWindow = new PopupWindow(popupView,
     LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

   Button btnDismiss = (Button) popupView.findViewById(R.id.btnPopUpOk);
   btnDismiss.setOnClickListener(new Button.OnClickListener() {
    public void onClick(View v) {
     popupWindow.dismiss();
    }
   });

   popupWindow.showAsDropDown(btnPopup, 50, -30);
  }
 }
}

MultiActivity demo

Ez a leírás azt a példát mutatja be, hogyan kell több Activity komponenst létrehozni illetve azokat elindítani illetve a log üzenetekből látható, hogy az egyes Activity komponensek "életciklus" eseményei hogyan keletkeznek.

Első Activity XML forrása:


    

A második és harmadik Activity UI felületét leíró XML-t az alábbi módon lehet létrehozni:
A Project Explorer-ben a res->layout könyvtáron kell egy jobb egér gombot nyomni, erre az alábbi menü jelenik meg. Itt a File menüt kell kiválasztani.

A menü kiválasztása után az alábbi dialódus ablak jelenik meg:

Ezt a műveletet annyiszor kell megismételni, ahány Activity komponenst szeretnénk létrehozni.

Második Activity XML forrása:


    

Harmadik Activity XML forrása:


    


A következő lépésben létre kell hozni az Activity layout-okhoz a kezelő osztályokat:
Ezt a Project Explorer-ben az src-> package könyvtáron kell egy jobb egér gombot nyomni, és itt a New Class menüt kell választani az alábbiak szerint:

A menüpont kiválasztása után az alábbi párbeszéd ablak jelenik meg:

A Name textbox-ba kell megadni az új osztály nevét. A befejezés után egy üres osztály deklarációt kapunk, aminek a kódját kiegészíthetjük úgy mint ahogy a MainActiviy osztályt létrehozta az Eclipse.

A MainActivity osztály definíciója:

package hu.globomax.multiactivity;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  Log.d("TEST", "MainActivity onCreate");
 }

 public void onClick(View v) {
  if (v.getId() == R.id.btnStartSecond) {
   Intent i = new Intent();
   i.setClass(this, SecondActivity.class);
   startActivity(i);
  }
 }

 @Override
 protected void onDestroy() {
  Log.d("TEST", "MainActivity onDestroy");
  super.onDestroy();
 }

 @Override
 protected void onPause() {
  Log.d("TEST", "MainActivity onPause");
  super.onPause();
 }

 @Override
 protected void onRestart() {
  Log.d("TEST", "MainActivity onRestart");
  super.onRestart();
 }

 @Override
 protected void onStart() {
  Log.d("TEST", "MainActivity onStart");
  super.onStart();
 }

 @Override
 protected void onStop() {
  Log.d("TEST", "MainActivity onStop");
  super.onStop();
 }

 @Override
 protected void onResume() {
  Log.d("TEST", "MainActivity onResume");
  super.onResume();
 }
}

A Második Activity osztály definíciója:

package hu.globomax.multiactivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.app.Activity;
import android.content.Intent;

public class SecondActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.sec);
  Log.d("TEST", "SecondActivity onCreate");
 }

 public void onClick(View v) {
  if (v.getId() == R.id.btnBackToFirst) {
   finish();
  } else if (v.getId() == R.id.btnStartThird) {
   Intent i = new Intent();
   i.setClass(this, ThirdActivity.class);
   startActivity(i);
  }
 }
 
 @Override
 protected void onDestroy() {
  Log.d("TEST", "SecondActivity onDestroy");
  super.onDestroy();
 }

 @Override
 protected void onPause() {
  Log.d("TEST", "SecondActivity onPause");
  super.onPause();
 }

 @Override
 protected void onRestart() {
  Log.d("TEST", "SecondActivity onRestart");
  super.onRestart();
 }

 @Override
 protected void onStart() {
  Log.d("TEST", "SecondActivity onStart");
  super.onStart();
 }

 @Override
 protected void onStop() {
  Log.d("TEST", "SecondActivity onStop");
  super.onStop();
 }
 
 @Override
 protected void onResume() {
  Log.d("TEST", "SecondActivity onResume");
  super.onResume();
 }
}

A Harmadik Activity osztály definíciója:

package hu.globomax.multiactivity;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class ThirdActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.third);
  Log.d("TEST", "ThirdActivity onCreate");
 }
 
 public void onClick(View v) {
  if (v.getId() == R.id.btnBackToSecound) {
   finish();
  }
 }
 
 @Override
 protected void onDestroy() {
  Log.d("TEST", "ThirdActivity onDestroy");
  super.onDestroy();
 }

 @Override
 protected void onPause() {
  Log.d("TEST", "ThirdActivity onPause");
  super.onPause();
 }

 @Override
 protected void onRestart() {
  Log.d("TEST", "ThirdActivity onRestart");
  super.onRestart();
 }

 @Override
 protected void onStart() {
  Log.d("TEST", "ThirdActivity onStart");
  super.onStart();
 }

 @Override
 protected void onStop() {
  Log.d("TEST", "ThirdActivity onStop");
  super.onStop();
 }
 
 @Override
 protected void onResume() {
  Log.d("TEST", "ThirdActivity onResume");
  super.onResume();
 }
}

Ezek után a projekt manifest állományában a második és harmadik Activity komponenseket is deklarálni kell.




    

    
        
            
                

                
            
        
        
        
        
        
    



2012. november 10., szombat

Képernyő méretek

Képernyő méretek minimum küszöb értékei Android-on.

  • xlarge: legalább 960dp x 720dp
  • large: legalább 640dp x 480dp
  • normal: legalább 470dp x 320dp
  • small: legalább 426dp x 320dp
A dp - (density-independent pixel) sűrűség független pixel. Egy dp egy fizikai pixelnek felel meg egy 160 dpi-s képernyőn.

px = dp * (dpi / 160)

2012. november 3., szombat

Acitvity életciklus modellje



public class ExampleActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Most jön létre az Activity
    }
    @Override
    protected void onStart() {
        super.onStart();
        // Most válik láthatóvá az Activity
    }
    @Override
    protected void onResume() {
        super.onResume();
        // Láthatóvá vált az Activity
    }
    @Override
    protected void onPause() {
        super.onPause();
        // Másik Activity veszi át a focus-t
        // (ez az Activity most kerül „Paused” állapotba)
    }
    @Override
    protected void onStop() {
        super.onStop();
        // Az Activity már nem látható
        // (most már „Stopped” állapotba van)
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Az Activity meg fog semmisülni
    }
}

Gomb esemény kezelés megvalósítása XML oldalról

Az alkalmazás felhasználói felületét leíró XLM-ben lehetőség van a felületeken megjelenő gomb(ok)hoz eseménykezelőt definiálni. Ehhez az XML-ben a Button node-hoz hozzá kell adni android:onClick attribútumot, amiben annak a függvénynek a nevét kell megadni, ami majd kezelni fogja a gombra történő kattintás eseményét. Ezt a res->layout->activity_main.xml -ben kell kifejteni.


Majd a MainActivity osztályban létre kell hozni egy olyan metódust, ami a gomb(ok) eseménykezelését fogja végezni.

public class MainActivity extends Activity {
    
    ...
    public void onClick(View v) {
        if (v.getId()==R.id.btnHello)
            Toast.makeText(this, "Test", Toast.LENGTH_LONG).show();
    }
    ...

Menü kezelés megvalósítása XML erőforrásból

A programban a menüpontok elemeit az activity_main.xml állomány írja le. A Project Explorer-ben az erőforrás könyvtárban a menu könyvtárban található XML állomány írja le a programban megjelenő menü felépítését. (Az alábbi ábra szemlélteti, hogy hol kell keresni a menüt leíró XML erőforrás állományt a projektben.)

Az XML állományban egy darab menüpontot az alábbi XML node ír le:

Ahol az android:id tulajdonság a menü egyedi azonosítóját tartalmazza, az android:title tulajdonság a menü pont megjelenített nevét tartalmazza és végül az android:orderInCategory attribútum pedig a menü elem sorrendjére vonatkozó információt tartalmaz.

A menük használatához és a menüpontok kiválasztásának eseménykezeléséhez az XML erőforrás szerkesztésen kívül még szükséges némi kódolás is.
Ehhez a MainAcitvity osztályban kell felülbírálni az Acitvity ősosztálytól örökölt metódusokat:

  • public boolean onCreateOptionsMenu(Menu menu) metódust kell felülbírálni ahoz, hogy a program elndulásakor az XML erőforrásban leírt menüt létrehozza ("XML-ből felfújja") az a program.
  • public boolean onOptionsItemSelected(MenuItem item) metódust kell felülbírálni ahhoz, hogy a menüpontok kiválasztására mi történjen a programban.
Eclipse-ben lehetőség van a menüből kiválasztani, hogy az ősosztály melyik metódusait szeretnénk felülbírálni a saját osztályunkban.
Első lépésben a Project Explorer-ben keressük ki a  MainAcitvity.java osztályunkat és nyissuk meg ezt az osztályt a szerkesztőben. Ezután a MainActivity.java állomány szerkesztő ablakában jobb egér gombbal el kell navigálni az alábbi ábrán látható módon az Override/Implement methods menüpontba.

A fenti menüpont kiválasztása után az alábbi ablak jelenik meg:
A fenti listában ki kell keresni az onCreateOptionsMenu elemet és az onOptionsItemSelected elemet és ki kell jelölni, majd az OK gombbal jóvá kell hagyni a kiválasztást.
A jóváhagyás után az Eclipse elkészíti a MainAcitvity.java osztályban a két fenti metódus törzsét, amit fel kell tölteni az általunk végrehajtani kívánt kóddal.
Alább egy minta kódrészlet található a használatára:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
 getMenuInflater().inflate(R.menu.activity_main, menu);
 return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
 switch (item.getItemId()) {
 case R.id.menu_1:
  Toast.makeText(this, "Menu1", Toast.LENGTH_LONG).show();
  break;
 case R.id.menu_2:
  Toast.makeText(this, "Menu2", Toast.LENGTH_LONG).show();
  break;
 default:
  break;
 }
  
 return true;
}

A fenti menüpontok az alábbi módon jelennek meg különféle rendszereken:
Gingerbread 2.3.3

JellyBean 4.1.2

2012. november 2., péntek

Android Log osztály használata nyomkövetésre

Az alkalmazásban különféle nyomkövetési illetve hibakeresési céllal lehet használni az Android beépített Log osztályát erre a feladatra.

Beépített Log osztály naplózásra használható főbb metódusai

  • v(String, String) (verbose)
  • d(String, String) (debug)
  • i(String, String) (information)
  • w(String, String) (warning)
  • e(String, String) (error)
Kódrészlet a használatra:

btnGuess.setOnClickListener(new OnClickListener() {
   
 public void onClick(View v) {
    
  Log.d("TEST_DEBUG", "Guess gomb megnyomva debug");
    
  Log.i("TEST_INFO", "Guess gomb megnyomva Infó");
    
  Log.v("TEST_VERBOSE", "Guess gomb megnyomva debug verbose");
    
  Log.e("TEST_ERROR", "Guess gomb megnyomva debug error");
  
  // további kód 
  }
 });

Eclipse-ben a LogCat ablakban az alábbi üzenetek jelennek meg a btnGuess gomb megnyomására:


"could not open avd config file config.ini..." hibaüzenet javítása

Ha az Android SDK-t olyan felhasználói profil alá telepítettük, ahol a felhasználói profil ékezetes karaktereket vagy szóközt tartalmaz, abban az esetben az AVD Manager-ben létrehozott emulátor indítása után az alábbi hibaüzenetbe ütközhetünk:

PANIC: Could not open AVD config file: C:\Users\Ékezetes_felhasználónév\.android\avd\Teszt.avd/config.ini

Ezt úgy lehet kiküszöbölni, hogy egy új környezeti változót kell létrehozni ANDROID_SDK_HOME névvel és a környezeti változóhoz az android SDK gyökér könyvtárának útvonalát kell megadni.
Ezt Win7 alatt az alábbi módon lehet beállítani:
Start menü->Vezérlőpult->Rendszer és Biztonság->Rendszer->Speciális rendszer beállítások
A megnyíló ablakban a Környezeti változók gombra kell kattintani (1. lépés).

Majd a megnyíló Környezeti változók ablakban az Új... feliratú gombra kell kattintani (2. lépés).
A Rendszerváltozó szerkesztése ablakba a változó neve mezőbe az ANDROID_SDK_HOME értéket kell beírni. A változó értékének pedig azt az útvonalat kell beírni, ahová az Android SDK van feltelepítve. A fenti példában ez a c:\android-sdk útvonal. A fenti beállítás után ha elindítjuk az AVD Manager programot, akkor az alábbi ábrán lévő útvonalat kell látnunk.

Eclipse gyorsbillentyűk

Ctrl+click: navigálás a kódban
Ctrl+space: intelli sense és „kódgenerálás” ha egy felüldefiniálandó metódus első pár betűjét ütjük le, majd Enter-t nyomunk
Alt+Shift+R: változó átnevezése (mindenhol)
Ctrl+Shift+T: típus keresése
F3: deklaráció megnyitása függvényen, osztályon
Ctrl+Alt+H: hívás hierarchia megjelenítése
Ctrl+Shift+O: import fix
Ctrl+Shift+F: forrás formázása
Ctrl+F6: forrásfájlok közti váltás
Ctrl+Shift+G: használat listázása
Ctrl+Shift+L: hotkey lista
Ctrl+PageUP/PageDown: Navigálás a megnyitott állományok között
Alt+Bal/Jobb: Ugrás a kód korábban használt szakaszára
Ctrl+1: Eclipse automatikus javaslat az adott helyen
Ctrl+Shift+R: erőforrás keresés
Alt+Shift+A, majd S: Szöveges konstans kihelyezése a szöveges értékeket tartalmazó Android strings.xml állományba

2012. október 5., péntek

Debug konzol ablakkal

Win32 alkalmazásban nyomkövetési funkcióra használhatunk konzol ablakot. Ehhez a főprogramban az AllocConsole WinApi hívást kell betenni. Ezután a programban a Write, WriteLn metódusok használhatók nyomkövetési célra.
program test_application;

uses
  ..
  Windows,
  ..;

{$R *.res}

begin
  AllocConsole;
  Application.Initialize;
  ...
  Application.Run;
  FreeConsole;
end.

2012. augusztus 7., kedd

Egyszerű "kitalálós" játék

Ez az egyszerű játék igazából a konzolról történő beolvasást demonstrálja. A példában kétféle beolvasási mód van: az egyik egy int szám beolvasása kbd.nextInt(), a másik egyetlen karakter beolvasása a konzolról kbd.findWithinHorizon(".", 0).charAt(0). Ezen kívül természetesen a Scanner osztálynak még számos más metódusa létezik.
package inputreading;

import java.util.Date;
import java.util.Random;
import java.util.Scanner;

public class InputReading {

    public static void main(String[] args) {
        // billentyűzet scanner
        Scanner kbd = new Scanner(System.in);
        
        char cAnswer; 
        int iTip;
        boolean bEnd;
        Date dtStart;
        Date dtEnd;
        
        do {
            dtStart = new Date();
            int iTryCnt = 0;
            // feladvány előállítása
            int iNum = new Random().nextInt(10) + 1;
            do {
                System.out.println(++iTryCnt + ". kísérlet");
                System.out.print("Írd be a tippet: ");
                // int szám beolvasása a konzolról
                iTip = kbd.nextInt();
            } while (iTip != iNum);

            dtEnd = new Date();
            
            Double dDifference = (dtEnd.getTime() - dtStart.getTime()) / 1000.0;
            System.out.println(dDifference + " másodperc alatt találtad ki a feladványt");
            System.out.println(iTryCnt + " lépésből találtad ki a számot");
            System.out.println("");

            do {
                System.out.println("Akarsz újra játszani? (i/n)");
                cAnswer = Character.toLowerCase(kbd.findWithinHorizon(".", 0).charAt(0));
            } while (cAnswer != 'i' && cAnswer != 'n');
            
            bEnd = (cAnswer == 'n');
            // ismételd addig, amíg bEnd true nem lesz
        } while (!bEnd);
    }
}

2012. augusztus 1., szerda

Form TopMost tulajdonságának beállítása


Néha szükség lehet arra, hogy egyes ablakok mindig legfelül "topmost" módon jelenjenek meg. Ezt egy egyszerű WinAPI trükkel lehet megoldani (persze létezik erre más módszer is).

procedure TfrmDlgCommonSyncProgress.FormShow(Sender: TObject);
begin
  SetWindowPos(
    Self.Handle,
    HWND_TOPMOST,
    0,
    0,
    0,
    0,
    SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
end;
Persze ugyanezt a hatást lehet elérni, ha az adott form CreateParams metódusának felülírásával is.

Flyweight minta alkalmazása

A Flyweight (pehelysúlyú) szerkezeti objektum minta megvalósítása Delphi alatt. Ezt a mintát valósítja meg a TCollection és TCollectionItem osztály. Ezt a gyakorlatban olyan esetekben szoktam használni, amikor dinamikusan összetett adatokat kell kezelni és a hagyományos tömb szerkezet ehhez nem nyújt kellő rugalmasságot.

A TCollection és TCollectionItem osztályokból származtatott saját konténer osztályok alkalmazásával ki lehet aknázni az OOP által nyújtott előnyöket mint például a kollekcióba szervezett adatok belső integritásának védelme, vagy az adatok állapot változásának esemény kezelése saját eseménykezelők használatával (pl. ha a kollekcióban egy elem állapota megváltozik, akkor egy eseménykezelőben kezelni lehessen a bekövetkezett változásokat).

A Flyweight minta megvalósítása örökléssel a TCollection és TCollectionItem osztályokból (ez csak egy kód csontváz, amit igazából az adott célnak megfelelően kell elkészíteni, kiegészíteni a feladathoz leginkább illeszkedő mezőkkel, metódusokkal) Kollekció elem csontváz osztály interface része:
interface

uses
  Classes, ...;

type
  TMyCustomDataItem = class(TCollectionItem)
  private
    // itt kell definiálni azokat a mezők tároló változóit,
    // amit majd a külvilág felé publikálni szeretnénk tulajdonságokon
    // keresztül
    FMyIntField    : Integer;
    FMyStringField : String;
    ...
  public
    constructor Create(Collection: TCollection); override;
    ...
    property MyIntField : Integer read FMyIntField write FMyIntField;
    property MyStringField : String read FMyStringField write FMyStringField;
    ...
  end;
A kollekció elem publikált mezőihez lehet getter/setter metódusokat definiálni, ezt a konkrét feladat dönti el, hogy mire van szükségünk. Ha például eseményt szeretnénk kiváltani, ha egy elemnek (TCollectionItem) megváltozik a belső állapota, akkor a figyelni kívánt tulajdonság setter metódusát kell "felokosítani" erre a feladatra, hogy az állapot változásról értesítse ki a kollekciót kezelő objektumot (TCollection). Kollekció csontváz osztály interface része:
  ...
  TMyCustomDataCollection = class(TCollection)
  private
    function GetMyCustomDataItem(Index : Integer) : TMyCustomDataItem;
    procedure SetMyCustomDataItem(Index : Integer; 
      const Value : TMyCustomDataItem);
  public
    destructor Destroy; override;

    function Add : TMyCustomDataItem; overload;
    function Add(AMyInt : Integer; AMyString : String) : TMyCustomDataItem; overload;

    procedure DeleteAll;
    procedure OrderByMyIntField;
    property Items[Index : Integer] : TMyCustomDataItem read GetMyCustomDataItem
      write SetMyCustomDataItem;
  end;
A fenti példában a TMyCustomDataCollection osztályt felruházom rendezés funkcióval, ami a MyIntField mező szerint fogja a kollekcióban az elemeket sorba rendezni, az összes elem törlése funkció, egyedi adatokkal történő a adat inicializálás Add(1, 'MyString'). TMyCustomDataItem osztály implementációs csontváza:
...
implementation
{ TMyCustomDataItem }

constructor TMyCustomDataItem.Create(Collection: TCollection);
begin
  inherited Create(Collection);
  FMyIntField := $F0F0;
  FMyStringField := '';
  ..
  // további inicializáló utasítások
end;
TMyCustomDataCollection kollekció implementációs csontváza:
{ TMyCustomDataCollection }

destructor TMyCustomDataCollection.Destroy;
begin
  DeleteAll;
  inherited Destroy;
end;

function TMyCustomDataCollection.Add: TMyCustomDataItem;
begin
  Result := inherited Add as TMyCustomDataItem;
end;

function TMyCustomDataCollection.Add(AMyInt : Integer; AMyString : String): TMyCustomDataItem;
begin
  Result := inherited Add as TMyCustomDataItem;
  Result.MyIntField := AMyInt;
  Result.MyStringField := AMyString;
  ..
  // további értékadó utasítások
end;

procedure TMyCustomDataCollection.DeleteAll;
begin
  while (Self.Count > 0) do
    Self.Delete(0);
end;

function TMyCustomDataCollection.GetMyCustomDataItem(Index: Integer): TMyCustomDataItem;
begin
  Result := inherited Items[Index] as TMyCustomDataItem;
end;

procedure TMyCustomDataCollection.SetMyCustomDataItem(Index: Integer;
  const Value: TMyCustomDataItem);
begin
  inherited Items[Index] := Value;
end;

procedure TMyCustomDataCollection.OrderBySessionNum;
var
  iIndex        : Integer;
  iIndex2       : Integer;
  pMinItem      : TMyCustomDataItem;
  pCurrItem     : TMyCustomDataItem;
begin
  // elemek rendezése FMyInt szám szerint növekvő sorrendben
  // a min sort algoritmusnál van gyorsabb rendezés is ;)
  for iIndex := 0 to Self.Count - 1 do
    begin
      pMinItem := Self.Items[iIndex];

      for iIndex2 := iIndex to Self.Count - 1 do
        begin
          pCurrItem := Self.Items[iIndex2];
          if pCurrItem.SessionNum < pMinItem.SessionNum then
            begin
              pCurrItem.Index := pMinItem.Index;
              pMinItem := pCurrItem;
            end;
        end;
    end;
end;
A fenti csontvázak alapján a saját igényeknek megfelelően kell tovább bővíteni a kollekció és kollekció elem osztályokat.

String írása/olvasása TMemoryStream-el

Az alábbi snipet egy String tartalmának írását olvasását szemlélteti egy TMemoryStream-ben. Ez nagyon hasznos tud lenni, ha nem akarunk tömbökkel bűvészkedni. Az alábbi kód töredéket gyakran szoktam használni.
procedure TForm1.btnStreamRWTestClick(Sender: TObject);
var
  msStream  : TMemoryStream;
  sData     : String;
begin
  sData := 'Ez egy tesz szöveg';
  msStream := TMemoryStream.Create;
  try
    msStream.Clear;
    // sData tartalmának kiírása az msStream MemoryStream-be
    msStream.WriteBuffer(Pointer(sData)^, Length(sData));

    sData := 'alma';

    // msStream tartalmának visszaolvasása a sData String-be
    // sData méret beállítása !!!
    SetLength(sData, msStream.Size);
    // SData tartalmának "nullázása"
    FillChar(sData[1], msStream.Size, 0);
    // pozicionálás a MemoryStream elejére olvasás előtt
    msStream.Position := 0;               
    msStream.ReadBuffer(Pointer(sData)^, msStream.Size);

  finally
    if Assigned(msStream) then
      begin
        msStream.Clear;
        msStream.Free;
      end;
  end;
end;

2012. július 31., kedd