Android - Jednoduché kreslení prstem #2

Java Android Android - Jednoduché kreslení prstem #2

Po tom, co jsme si v předchozím díle ověřili funkčnost události onTouch můžeme přejít ke kreslení.

Teď začneme z gruntu. Ověřili jsme si, že nám funguje získávání souřadnic a můžeme přejít k samotnému kreslení.

Kódění - Jdem to naťukat

V hlavní třídě MainActivity.java si nechte jen základ:

package cz.devbook.aepaint;

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

public class MainActivity extends Activity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
}

Z layoutu rovněž odmažte přidaný textView. Napíšeme si totiž vlastní, na kterém budeme zobrazovat naše čárance.

Vytvořme si nyní novou třídu například MyCanvas. Pravým klikem na package projektu - Add/Class. Zde vyplníme pouze název a potvrdíme.

Vytvoří se nám prázdná třída.

package cz.devbook.aepaint;

public class MyCanvas
{

}

Klíčovým slůvkem extends podědíme View.

public class MyCanvas extends View

Klepnutím zase přidáme import a necháme za nás IDE i doplňit konstruktor (ten první s parametrem Context).

Vytvoříme třídní proměnnou paint typu Paint

private Paint paint = new Paint();

Obsahuje vlastnosti k nastavení pera. To si teď nastíníme a nastavíme v kontruktoru naší nové třídy.
Pod fcí super, kterou se volá kontruktor předka si nastavíme pero. Můžete použít jaké chcete nastavení, zde si vysvětlíme co která metoda dělá.

Nastavení barvy pera. Výchozí je černá. Zadává se jako ukazatel ve formátu 0xffxxxxxx, kde x reprezentuje klasické HTML RGB. Po naimporotvání android.graphic­s.Color; lze volit barvy příjemnější cestou.

paint.setColor(Color.RED);

Zapíná vyhlazení.

paint.setAntiAlias(true);

Určuje styl vykreslení. Protože budeme používat cestu, kterou když namalujeme tak se automaticky uzavře, tak použijeme STROKE což je okraj - to nám zobrazí jen křivku, kterou kreslíme.

paint.setStyle(Paint.Style.STROKE);

Zaoblení koncové a začáteční špičky křivky, v ohybu...
Zde je to hezky znázorněno: http://www.croczilla.com/…nestroke.xml

paint.setStrokeCap(Cap.ROUND);
paint.setStrokeJoin(Join.ROUND);
paint.setStrokeMiter(.5f);

Nastavení tloušťky pera v pixelech

paint.setStrokeWidth(5);

Můžete vyzkoušet i další, tyhle však považuji za nejpřínosnější. A použil jsem je tak, jak jsou zde uvedeny.

Nyní si vytvoříme další třídní proměnnou path typu Path. Ta bude užita pro uchovávání souřadnic cesty.

private Path path = new Path();

Přidáme událost onTouchEvent, kterou již známe. Lehce ji však pozměníme.

@Override
public boolean onTouchEvent(MotionEvent event)
{
        // Deklarace proměnných pro ukládání pozice dotyku
        float xPos = event.getX();
        float yPos = event.getY();

        // Zpracování akcí
        switch (event.getAction())
        {
                // Při dotyku, se vyresetuje výchozí pozice
                case MotionEvent.ACTION_DOWN:
                        path.moveTo(xPos, yPos);
                break;

                // Při pohybu nebo opuštění obrazovky
                case MotionEvent.ACTION_MOVE:
                case MotionEvent.ACTION_UP:
                        // Nastaví se současné souřadnice
                        path.lineTo(xPos, yPos);
                break;
        }

        // Překreslení
        invalidate();

        return true;
}

Teď ještě přidáme rychlý event vykreslování onDraw.

@Override
public void onDraw(Canvas canvas)
{
        canvas.drawPath(path, paint);
}

Přepneme se do hlavní třídy a vytvoříme třídní proměnnou myCanvas.

private MyCanvas myCanvas;

Které v metodě onCreate přiřadíme instanci naší třídy a použijeme ji.

@Override
public void onCreate(Bundle savedInstanceState)
{
        super.onCreate(savedInstanceState);

        // Přepnutí zobrazování na naši vlastní třídu
        myCanvas = new MyCanvas(this);
        setContentView(myCanvas);
}

Nyní aplikaci vyzkoušíme.

Pokud nám kreslení funguje v pořádku, můžete si vyzkoušet měnit nějaké hodnoty a pohrát si z nastavením. Pak bdeme pokračovat. Ještě přidáme nějaké drobnůstky.

Kódění - Multi-touch, čištění plátna, pero

Protože jsme nároční, přidáme si jednoduše možnost multi-touche.

Pomocí následujícího kódu si zjistíme kolik prstů je na dyspleji.

final int pointersCount = event.getPointerCount();

A poté jen projedeme cyklem a přiřazujeme jejich pozice.

for (int p = 0; p < pointersCount; p++)
{
        xPos = event.getX(p);
        yPos = event.getY(p);
        ...

A ještě po pouhém klepnutí tečku. Tu jednoduše do ACTION_DOWN přidáním pohybu o jeden pixel. (Mě dycky štvalo, ťukat a netečkovat)

path.lineTo(xPos + 1, yPos + 1);

Celý kód metody:

@Override
public boolean onTouchEvent(MotionEvent event)
{
        // Inicializace proměnných pro ukládání pozice dotyku
        float xPos;
        float yPos;
        // Počet prstů, kterými se dotýkáme obrazovky. Maximum je (asi v závislosti na zařízení) limitováno na 4
        final int pointersCount = event.getPointerCount();

        // Projede všechny pointery - umožňuje multi-touch
        for (int p = 0; p < pointersCount; p++)
        {
                xPos = event.getX(p);
                yPos = event.getY(p);
                // Zpracování akcí
                switch (event.getAction())
                {
                        // Při dotyku, se vyresetuje výchozí pozice
                        case MotionEvent.ACTION_DOWN:
                                path.moveTo(xPos, yPos);
                                path.lineTo(xPos + 1, yPos + 1);
                        break;

                        // Při pohybu nebo opuštění obrazovky
                        case MotionEvent.ACTION_MOVE:
                        case MotionEvent.ACTION_UP:
                                // Nastaví se současné souřadnice
                                path.lineTo(xPos, yPos);
                        break;
                }
        }

        // Překreslení
        invalidate();

        return true;
}

Teď už se s tím dá pěkně vyhrát :) Nyní si vytvoříme metodu na čištění plátna. Nazvěme ji clearCanvas Jednoduše vymažeme souřadnice cesty a překreslíme.

public void clearCanvas()
{
        path.reset();
        invalidate();
}

Nyní se naučíme pracovat s menu. Nejprve přidáme do string.xml "Ukončit" a "Vyčistit", které pojmenujte třeba exit, clear s prefixem menu_.
Pak si rozklepněte složku menu a vní jediný xml soubor.
Odmažeme stávající položky a přidáme si vlastní. Klepnutím na add přidáme item.
Po dlouhé době přidávám obrázek :)

Napravo v "properties" pak nastavíme jen ID a Titulek. Id pouze přejmenujeme na něco v podobném duchu, abychom se v tom vyznali.

@+id/clear
@+id/exit

A do title vybereme daný string

@string/menu_clear
@string/menu_exit

Uspořádání lze měnit pomocí tlačítek UP/DOWN.

Nyní pokud klepneme na tlačítko MENU na telefonu v naší aplikaci tak se nám zobrazí menu. Nyní jej zaktivníme.

Do hlavní třídy přidáme metodu onOptionsItem­Selected a do ní následující kód.

@Override
public boolean onOptionsItemSelected(MenuItem item)
{
        // získání ID vybrané položky
        switch (item.getItemId())
        {
                // Ukončení aplikace
                case R.id.exit:
                        this.finish();
                return true;

                // Vyčištění plátna
                case R.id.clear:
                        myCanvas.clearCanvas();
                return true;

                default:
                        return super.onOptionsItemSelected(item);
        }
}

Nyní když aplikaci vyzkoušíme lze již malovat, hrát si s prsty a mazat plátno.

Ještě si uděláme možnost výběru barvy a tloušťky, se kterou budeme kreslit.

Do MyCanvas přidáme tyto dvě metůdky:

public void setPenColor(int color)
{
        paint.setColor(color);
        invalidate();
}

public void setPenWidth(float width)
{
        paint.setStrokeWidth(width);
        invalidate();
}

Přidáme texty a položky do menu. V metodě onOptionsItem­Selected v hlavní třídě si je pak zaktivníme:

case R.id.pen_width:
        final CharSequence[] sizes = {"1", "3", "5", "10", "15", "20"};

        // Vytvoření dialogu
        AlertDialog.Builder sizePickerDialog = new AlertDialog.Builder(this);
        sizePickerDialog.setTitle("Vyber tloušťku:");
        sizePickerDialog.setItems(sizes, new DialogInterface.OnClickListener()
        {
                // Po zvolení se zobrazí informace o vybrané položce a zavolá se příslušná metoda, která nastaví vybranou vlastnost
                public void onClick(DialogInterface dialog, int item)
                {
                        Toast.makeText(getApplicationContext(), "Vybrána tloušťka: " + sizes[item] + "px", Toast.LENGTH_SHORT).show();
                        myCanvas.setPenWidth(Float.valueOf((String)sizes[item]));
                }
        });
        AlertDialog pickSize = sizePickerDialog.create();
        pickSize.show();
return true;
case R.id.pen_color:
        final HashMap<String, Integer> colorList = new HashMap<String, Integer>();
        colorList.put("Černá", Color.BLACK);
        colorList.put("Červená", Color.RED);
        colorList.put("Žlutá", Color.YELLOW);
        colorList.put("Zelená", Color.GREEN);
        colorList.put("Modrá", Color.BLUE);
        colorList.put("Fialová", Color.MAGENTA);
        colorList.put("Šedá", Color.GRAY);

        final CharSequence[] colors = colorList.keySet().toArray(new CharSequence[colorList.size()]);

        // Vytvoření dialogu
        AlertDialog.Builder colorPickerDialog = new AlertDialog.Builder(this);
        colorPickerDialog.setTitle("Vyber barvičku:");
        colorPickerDialog.setItems(colors, new DialogInterface.OnClickListener()
        {
                // Po zvolení se zobrazí informace o vybrané položce a zavolá se příslušná metoda, která nastaví vybranou vlastnost
                public void onClick(DialogInterface dialog, int item)
                {
                        Toast.makeText(getApplicationContext(), "Vybraná barvička: " + colors[item], Toast.LENGTH_SHORT).show();
                        myCanvas.setPenColor(colorList.get(colors[item]));
                }
        });
        AlertDialog pickColor = colorPickerDialog.create();
        pickColor.show();
return true;

Docela mě zamrzelo, že se v čistém SDK nenachází ColorPicker Dialog nebo NumberPicker Dialog. Nějaký NumberPicker je dostupný v SDK verze 11 což je verze nad 2.3.3 a pěkné ColorPickery jsem viděl jen v přídavných knihovnách. Tak jsem zaimprovizoval a přidal alespoň takto jednoduché dialogové listy s možnostmi.

Ještě bych rád zmínil, že pokud něco odlazujete a chcete kontrolovat hodnoty nebo prostě si něco vypsat, můžete vypisovat do debug konzole označené jako LogCat pomocí:

Log.d(tag, msg);

Tag označuje čeho se to týká a msg je zpráva. Obojí se vypíše do okýnka LogCat.

Nyní nám aplikace splňuje naše minimální nároky a můžeme si hrát, črtat, kreslit atp.

Určitě si sami zkuste aplikaci doplnit například o změnu pozadí. Zkuste si dát maximální SDK (AndroidManifes­t.xml) a přidat NumericPicker či ColorPicker. Zkuste popřemýšlet jak vyřešit vracení se o kroky zpět. Jak to udělat aby se při změně barvy nezměnili všechny čáry. Abychom při natočení mobilu neztratili data. Přidat možnost kreslení kružnic, čtverců... Ukládání a mnohé další.

Rád vám pomohu a společně na fóru prográmek vymakáme a pak se o něj můžete podělit :)


 

Stáhnout

Staženo 867x (652.15 kB)
Aplikace je včetně zdrojových kódů v jazyce Android Java

 

  Aktivity (1)

Článek pro vás napsal David Jančík [sczdavos]
Avatar
Autor je vášnivý programátor v .NET C# a PHP. Nezná slovo "nelze", nebojí se zkoušet nepoznané a pronikat do nových technologií.

Jak se ti líbí článek?
Celkem (3 hlasů) :
333 33


 


Miniatura
Všechny články v sekci
Programování Android aplikací v Javě
Miniatura
Následující článek
Mobilní telefony

 

 

Komentáře

Avatar
jozef.dobrovolny:

Perfecktné!:)

 
Odpovědět 26.1.2013 10:48
Avatar
teeg
Člen
Avatar
teeg:

Ahoj, ten multitouch ti nefunguje. Zkoušel jsem stáhnout i tvé zdrojáky a je tam ten stejný problém. Tablet sice zaregistruje více dotyků ale nekreslí dvě čáry, ale kreslí takovou divnou křivku(jak kdyby spojoval ty dva prsty). Jakmile se dotknu dvěma prsty, tak to mezi nimi udělá spojnici. (u třech trojúhelník, ...) Nevíš, jak by to šlo ošetřit?

 
Odpovědět 22.4.2013 13:31
Avatar
Odpovídá na teeg
David Jančík [sczdavos]:

Ahoj, šak to bylo záměrem. Tjo, já už jsem to úplně zapomněl, jsem pro android zkoušel kódit asi 2 měsíce tak jsem sepsal nějaké poznatky a nějak mě to nenadchlo. Co se týče mobilních zařízení v budoucnu se možná podívám na Okna nebo Jablíčko ale spíš to vidím na HTML5 a JS, aplikace jako takové (přímo pro platformu, desktop etc) už moc nefrčí, takže jdu teď cestou webaplikací. Ještě dodělávám nějaké projekty v C# a pak se jdu naplno věnovat PHP a JS.

Odpovědět 22.4.2013 14:55
Čím více času dostaneš, tím méně ho máš.
Avatar
teeg
Člen
Avatar
teeg:

Aha, tak to jsem asi špatně pochopil ;-). Díky za info.

 
Odpovědět 22.4.2013 16:05
Avatar
Zdeněk Pavlátka
Tým ITnetwork
Avatar
Zdeněk Pavlátka:

Jak vypadá konstruktor třídy MyCanvas? Mám jiné IDE (přímo na androidu) a to konstruktory negeneruje.

Odpovědět 4.1.2014 13:38
Kolik jazyků umíš, tolikrát jsi programátor.
Avatar
Zdeněk Pavlátka
Tým ITnetwork
Avatar
Odpovědět 4.1.2014 17:07
Kolik jazyků umíš, tolikrát jsi programátor.
Avatar
Zdeněk Pavlátka
Tým ITnetwork
Avatar
Odpovídá na David Jančík [sczdavos]
Zdeněk Pavlátka:

V článku máš chybu - máš tam

paint.setStrokeCap(Cap.ROUND);
paint.setStrokeJoin(Join.ROUND);

místo

paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeJoin(Paint.Join.ROUND);
Odpovědět 4.1.2014 17:11
Kolik jazyků umíš, tolikrát jsi programátor.
Avatar
Neaktivní uživatel:

Ahoj, nevíte, čím by mohlo být, že mi eclipce negeneruje soubory? Na začátku se to zeptá, jak se má jmenovat hlavní aktivita atd. ale pak to vygeneruje projekt bez balíčku a bez layoutu. Ne že by to nešlo udělat ručně, ale už mě to nebaví.
Díky za rady
P.S. Používám Eclipse Kepler na W7

Odpovědět 29.6.2014 20:10
Neaktivní uživatelský účet
Avatar
siders
Člen
Avatar
siders:

čau, kód jsem zkoušel ne přímo v Eclipse, ale v Android Studiu a všechno jede, jak má :) jediné, co mi trochu chybí, je možnost uložení kresby, ale už hledám, jak na to :) velký dík autorovi tohoto článku ;)

 
Odpovědět 27.4.2015 10:55
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 9 zpráv z 9.