Рисование на канве

Ответить
Аватара пользователя
andev
Сообщения: 219
Зарегистрирован: 13 янв 2012, 17:56

Рисование на канве

Сообщение andev » 07 апр 2012, 19:17

Появилась необходимость вставить в разрабатываемое приложение следующий функционал: есть некое изображение (задник), оно залито одноцветным полем. Когда водишь пальцем по этому полю, оно стирается и задник "проявляется". Аналог из реальной жизни - карточки пополнения мобильного счета.
Куда копать? может кто-то сталкивался с подобным функционалом в любом приложении - скачаю и декомпилирую, хотя бы layout гляну.
нужно очень срочно, за любую помощь отблагодарю

Аватара пользователя
KamiSempai
Сообщения: 1339
Зарегистрирован: 17 фев 2012, 21:23
Откуда: Мордор

Re: Рисование на канве

Сообщение KamiSempai » 08 апр 2012, 21:25

Код: Выделить всё

mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
Более подробно можно посмотреть в Гуголовской демке "API Demos".
Пакет: com.example.android.apis.graphics
Класс: FingerPaint
R.id.team
Хватит таскать макулатуру на тренировку! Используй T Note.

Аватара пользователя
andev
Сообщения: 219
Зарегистрирован: 13 янв 2012, 17:56

Re: Рисование на канве

Сообщение andev » 09 апр 2012, 02:09

Спасибо, оттуда и начал. Естественно, наткнулся на проблему черного цвета при стирании. После дальнейшего поиска вроде бы нашел в точности то, что мне требуется, но пока не получается запустить.
http://groups.google.com/group/android- ... 173ba443a6

Аватара пользователя
andev
Сообщения: 219
Зарегистрирован: 13 янв 2012, 17:56

Re: Рисование на канве

Сообщение andev » 09 апр 2012, 03:17

Вроде, сделал все как у него:
main.xml

Код: Выделить всё

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:id="@+id/rl"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" >
    
    
</RelativeLayout>
MainActivity.java

Код: Выделить всё

package ru.test.CanvasTest2;

import ru.test.CanvasTest2.R.drawable;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;

public class MainActivity extends Activity {
	
	private Paint mPaint;
	
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setColor(0xFFFF0000);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(12);
        
        RelativeLayout rl = (RelativeLayout) findViewById(R.id.rl);
        
        ImageView iv = new ImageView(this);
        iv.setImageResource(drawable.ic_launcher);
        rl.addView(iv);
        
        MyImageView miv = new MyImageView(this);
        miv.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_red));
        
        rl.addView(miv);
    }
    
    public class MyImageView extends ImageView {

    	private Paint paint;
    	private Canvas c2;
    	private Bitmap overlay;
    	private Path path;
    	
		public MyImageView(Context context) {
			super(context);
			
			paint = new Paint();
			path = new Path();
			
			paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); 
            paint.setColor(Color.TRANSPARENT); 
            paint.setStyle(Paint.Style.STROKE); 
            paint.setStrokeJoin(Paint.Join.ROUND); 
            paint.setStrokeWidth(12); 
            paint.setAntiAlias(true);
		}
    	
		@Override
		public void setImageBitmap(Bitmap bm) {
			 super.setImageBitmap(bm);
			 Drawable bd = getDrawable(); 
             if(bd == null) {
            	 return; 
             } 

             Bitmap fullSizeBitmap = ((BitmapDrawable) bd).getBitmap(); 
             overlay = fullSizeBitmap.copy(Config.ARGB_8888, true);
             c2 = new Canvas(overlay);
		}
		
		@Override
		protected void onDraw(Canvas canvas) {
			//super.onDraw(canvas);
			
			Drawable bd = getDrawable(); 
            if(bd == null) {
            	return; 
            } 
            
            Bitmap fullSizeBitmap = ((BitmapDrawable) bd).getBitmap(); 
            if(fullSizeBitmap != null && c2 != null) {
            	canvas.drawColor(Color.TRANSPARENT); 
                c2.drawBitmap(fullSizeBitmap, 0, 0, null); 
                c2.drawPath(path, paint); 
                canvas.drawBitmap(overlay, 0, 0, null); 
            }
	}
    }
}
Кто разбирается, гляньте по возможности, пожалуйста.

Аватара пользователя
KamiSempai
Сообщения: 1339
Зарегистрирован: 17 фев 2012, 21:23
Откуда: Мордор

Re: Рисование на канве

Сообщение KamiSempai » 09 апр 2012, 07:36

Я, конечно, пока графикой не занимался и сказать, что разбираюсь - громко сказано :)
Может не использовать Path, а сразу рисовать линию от старой точки к новой в onTouchEvent?
Кстати, где onTouchEvent? Или можно без него?

PS: Перешел по ссылке, у парня та же проблема с черным цветом.
R.id.team
Хватит таскать макулатуру на тренировку! Используй T Note.

Аватара пользователя
andev
Сообщения: 219
Зарегистрирован: 13 янв 2012, 17:56

Re: Рисование на канве

Сообщение andev » 09 апр 2012, 11:36

Я что-то совсем запутался с этим Canvas'ом :(

Аватара пользователя
andev
Сообщения: 219
Зарегистрирован: 13 янв 2012, 17:56

Re: Рисование на канве

Сообщение andev » 09 апр 2012, 17:02

Изначально хотел подложку закрывать защитным слоем и, стирая защитный слой, "проявлять" лежащую под ним подложку. Но там были проблемы со стиранием с черными полосами которые, насколько показал поиск, никто не решил, даже на стековерфлове.
Сделал немного по-другому. Сначала кладу на канву защитный слой, подложку (которая теперь на самом деле и не подложка) просто держу в памяти. На событии ontouchEvent при action == Move делаю следующее
1) вычисляю координаты прикосновения,
2) беру с подложки ту часть изображения, которая соответствует этим координатам, размером 30 на 30 к примеру.
3) paintBitmap() этим кусочком изображения на защитный слой.
Хотя фактически внизу защита, а сверху кусок скрываемого изображения (по логике должно быть наоборот), в результате выглядит и отрабатывает адекватно.

Теперь о проблемах - не могу сообразить как мне это все культурно оформить что бы можно было вставить в готовую программу. Хотелось бы, что бы это была отдельная активити, на которой это изображение крупно по центру, снизу 1 кнопка.
Встает ряд проблем:
1) координаты прикосновения абсолютные на экране, а разместить надо в центре (либо учитывать смещение, либо пытаться получить относительные координаты)
2) во что это все красиво упаковать что бы было удобно пользоваться?

Проект: http://zalil.ru/33043472

Аватара пользователя
KamiSempai
Сообщения: 1339
Зарегистрирован: 17 фев 2012, 21:23
Откуда: Мордор

Re: Рисование на канве

Сообщение KamiSempai » 09 апр 2012, 22:16

getLocationOnScreen (int[] location) - записывает в location координаты View.
Где location - массив из 2 элементов.
getMeasuredWidth() и getMeasuredHeight() вычисляют размеры.
R.id.team
Хватит таскать макулатуру на тренировку! Используй T Note.

Аватара пользователя
andev
Сообщения: 219
Зарегистрирован: 13 янв 2012, 17:56

Re: Рисование на канве

Сообщение andev » 09 апр 2012, 22:21

спасибо, буду использовать
и еще один вопрос вдогонку: drawBitmap() естественно рисует rectangle (x, y, width, height), как бы сделать, что бы рисовало circle'ом, например?

Аватара пользователя
KamiSempai
Сообщения: 1339
Зарегистрирован: 17 фев 2012, 21:23
Откуда: Мордор

Re: Рисование на канве

Сообщение KamiSempai » 10 апр 2012, 11:58

Только-что, за пол часа переделал FingerPaint и получил то, что нужно. Все ка я и предполагал. Будет время, оформлю как класс и выложу здесь.
R.id.team
Хватит таскать макулатуру на тренировку! Используй T Note.

Аватара пользователя
andev
Сообщения: 219
Зарегистрирован: 13 янв 2012, 17:56

Re: Рисование на канве

Сообщение andev » 10 апр 2012, 14:01

KamiSempai писал(а):получил то, что нужно
а что было нужно? стирание слоя без заливки цветом бэкграунда?
В любом случае очень жду

Аватара пользователя
KamiSempai
Сообщения: 1339
Зарегистрирован: 17 фев 2012, 21:23
Откуда: Мордор

Re: Рисование на канве

Сообщение KamiSempai » 10 апр 2012, 17:29

Собственно, вот сам класс:

Код: Выделить всё

package ru.KamiSempai.LotteryLayer;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class LotteryLayer extends View {
	private final static int COATING_COLOR = Color.DKGRAY;
	private final static int PAINT_WIDTH = 24;

	private Paint mPaint;
	private Bitmap mBitmap;
	private Canvas mCanvas;
	private Paint mBitmapPaint;

	public LotteryLayer(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		initialise();
	}

	public LotteryLayer(Context context, AttributeSet attrs) {
		super(context, attrs);
		initialise();
	}

	public LotteryLayer(Context context) {
		super(context);
		initialise();
	}

	private void initialise() {
		mBitmapPaint = new Paint(Paint.DITHER_FLAG);
		mPaint = new Paint();
		mPaint.setStyle(Paint.Style.STROKE);
		mPaint.setStrokeJoin(Paint.Join.ROUND);
		mPaint.setStrokeCap(Paint.Cap.ROUND);
		mPaint.setStrokeWidth(PAINT_WIDTH);
		mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
	}

	private Bitmap getBitmap() {
		if (mBitmap == null) {
			mBitmap = Bitmap.createBitmap(getMeasuredWidth(),
					getMeasuredHeight(), Bitmap.Config.ARGB_8888);
			mCanvas = new Canvas(mBitmap);
			mCanvas.drawColor(COATING_COLOR);
		}
		return mBitmap;
	}

	@Override
	protected void onDraw(Canvas canvas) {
		canvas.drawBitmap(getBitmap(), 0, 0, mBitmapPaint);
	}

	private float mX, mY;

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		float x = event.getX();
		float y = event.getY();

		if (event.getAction() == MotionEvent.ACTION_MOVE) {
			mCanvas.drawLine(mX, mY, x, y, mPaint);
			invalidate();
		}
		mX = x;
		mY = y;
		return true;
	}
}
С размером кисти и цветом заливки заморачиваться не стал и сделал их константами. Если делать все по совести их стоило бы вывести в атрибуты.

Файл разметки main.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:background="@drawable/ic_launcher" >
    <ru.KamiSempai.LotteryLayer.LotteryLayer
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</FrameLayout>
В самом активити ни чего делать не надо.

Готов ответить на любые вопросы, если таковые появились.

PS: Админ, не мешало бы на форуме спойлеры добавить :)
R.id.team
Хватит таскать макулатуру на тренировку! Используй T Note.

Аватара пользователя
andev
Сообщения: 219
Зарегистрирован: 13 янв 2012, 17:56

Re: Рисование на канве

Сообщение andev » 11 апр 2012, 01:28

Ух, здорово! Сейчас если не усну, попытаюсь оформить при помощи SurfaceHolder и попутно разобраться в чем у меня была проблема. Спасибо за то, что нашли время посмотреть и запостить :)

Аватара пользователя
KamiSempai
Сообщения: 1339
Зарегистрирован: 17 фев 2012, 21:23
Откуда: Мордор

Re: Рисование на канве

Сообщение KamiSempai » 11 апр 2012, 07:20

andev писал(а):... попытаюсь оформить при помощи SurfaceHolder ...
Думаю так будет на много лучше. Можно будет избавиться от getBitmap().
R.id.team
Хватит таскать макулатуру на тренировку! Используй T Note.

Аватара пользователя
andev
Сообщения: 219
Зарегистрирован: 13 янв 2012, 17:56

Re: Рисование на канве

Сообщение andev » 11 апр 2012, 11:34

А каким образом можно стирать canvas у уже существующего View?
Например, есть FrameLayout, на нем лежат друг на друге два ImageView, каждый с изображением. Я хочу "стирать" изображение с верхнего ImageView. Насколько я понимаю, мне нужно унаследовать класс ImageView - MyImageView, переопределить в нем onDraw, onTouchEvent, а также один из методов установки изображения, к примеру setImageBitmap или setImageResouce
Так должно получиться?

Аватара пользователя
KamiSempai
Сообщения: 1339
Зарегистрирован: 17 фев 2012, 21:23
Откуда: Мордор

Re: Рисование на канве

Сообщение KamiSempai » 11 апр 2012, 12:38

Не знаю тонкостей работы ImageView. Наверно можно.
Только при рисовании изображения на mCanvas скорее всего придется растягивать его по размерам ImageView.
А еще, можно взять родителем SurfaceView, тогда mCanvas совсем не нужен будет, и переопределять onDraw тоже, кажется.
R.id.team
Хватит таскать макулатуру на тренировку! Используй T Note.

Ответить