I created a custom view to draw some shapes on it but my problem is that nothing is showed on the view. I get no error messages or crash. My custom class looks the like:
public class DrawableView extends View {
private Paint mPaint;
private MyDrawable mDrawableObj; // object to draw
private RectF mRectBounds;
public DrawableView(Context context) {
super(context);
init();
}
public DrawableView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DrawableView(Context context, AttributeSet attrs, int defStyleAttr){
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int w = MeasureSpec.getSize(widthMeasureSpec);
int h = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(w, h);
}
public void setDrawableObj(MyDrawable obj, boolean forcePrepare) {
final boolean needPrepare = forcePrepare || (mDrawableObj != null) ;
mDrawableObj = obj;
mDrawableObj.setView(this);
if (needPrepare) {
prepareObject();
invalidate();
}
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
final int padLeft = getPaddingLeft();
final int padTop = getPaddingTop();
final float xpad = (float) (padLeft + getPaddingRight());
final float ypad = (float) (padTop + getPaddingBottom());
final float ww = (float) w - xpad - 1;
final float hh = (float) h - ypad - 1;
mRectBounds = new RectF(0, 0, ww, hh);
mRectBounds.offsetTo(padLeft, padTop);
prepareObject();
}
public void prepareObject() {
if (mDrawableObj != null)
mDrawableObj.prepare(mRectBounds);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDrawableObj != null)
mDrawableObj.draw(canvas, mRectBounds);
else {
canvas.drawRect(mRectBounds, mPaint);
}
}
My layout file is the following:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:id="#+id/mainLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hello.myApp.Draw">
<DrawableView.DrawableView
android:id="#+id/drawtest_drawableView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/toolbar"
app:layout_constraintLeft_toRightOf="#+id/leftMenu"
android:padding="4dp" />
</android.support.constraint.ConstraintLayout>
Do you have any idea why I have just a white screen?
use this
public void init() {
if (isInEditMode())
return;
//code
//...
}
I copy/pasted your code into my IDE and ran it (though I had to create a fake MyDrawable interface to satisfy the compiler). I'm just putting an instance of DrawableView into a LinearLayout and I see it drawing just fine.
My layout xml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/myLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.stackoverflow.DrawableView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="12dp"/>
</LinearLayout>
What I see:
It makes sense that I see only the blue rectangle, because this is your onDraw() implementation:
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDrawableObj != null)
mDrawableObj.draw(canvas, mRectBounds);
else {
canvas.drawRect(mRectBounds, mPaint);
}
}
Given that setDrawableObj(MyDrawable obj, boolean forcePrepare) is never called in the code you posted, the else is always what's being executed/drawn.
Related
I've two imageview of dots in my activity. I can move one of them and I'd like to show the path between the two dots. Any suggestions?
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
final float eventX = event.getRawX();
final float eventY = event.getRawY();
if(dotEnabled){
dot.setX(eventX);
dot.setY(eventY);
}
if(directionSet){
direction.setX(eventX);
direction.setY(eventY);
}
return false;
}
Add some properties to your paint:
Paint paint = new Paint();
paint.setStrokeWidth(30); //here you can put any width you need
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
If above code doesn't work below this part of code:
super(context);
paint.setColor(Color.BLUE);
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
call invalidate();
The easiest way is to extend FrameLayout, because you already use it as the outer ViewGroup in your layout.
To make this work, you have to declare two more constructors. The standard constructor is not used, when you declare an element in XML. So you have to set your Paint in all three of these constructors. To reduce redundancy, I moved this to a private method 'initiatePaint()'.
The function 'declarePath()' has to be called, when a new location for 'setDirection' is known.
public class DrawLayout extends FrameLayout {
Paint mPaint = new Paint();
float mX1 = 0;
float mY1 = 0;
float mX2 = 0;
float mY2 = 0;
public DrawLayout(Context context) {
super(context);
this.initiatePaint();
}
public DrawLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.initiatePaint();
}
public DrawLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.initiatePaint();
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(x1, y1, x2, y2, paint);
}
public void declarePath(float x1, float y1, float x2, float y2) {
this.mX1 = x1;
this.mY1 = y1;
this.mX2 = x2;
this.mY2 = y2;
this.invalidate();
}
private void initiatePaint() {
this.mPaint.setColor(Color.BLUE);
this.mPaint.setStrokeWidth(10);
this.mPaint.setStyle(Paint.Style.STROKE);
}
}
To use it in your layout, just use it instead of FrameLayout.
<com.example.DrawLayout
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:id="#+id/fl"
tools:context="it.uniroma3.sensorlog">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#mipmap/mappa"
android:id="#+id/map"/>
<ImageView
android:layout_width="10dp"
android:layout_height="10dp"
android:src="#android:drawable/ic_notification_overlay"
android:id="#+id/dot" />
<ImageView
android:layout_width="10dp"
android:layout_height="10dp"
android:visibility="invisible"
android:src="#android:drawable/ic_notification_overlay"
android:id="#+id/setDirection" />
</DrawLayout>
In your Activity, you have to do something:
#Override
protected void onCreate(Bundle savedInstanceState) {
[...]
this.mBackground = (DrawLayout) findViewById(R.id.fl);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
final float eventX = event.getRawX();
final float eventY = event.getRawY();
if(dotEnabled){
dot.setX(eventX);
dot.setY(eventY);
}
if(directionSet){
direction.setX(eventX);
direction.setY(eventY);
}
this.mBackground.declarePath(**Here you have to pass the coordinates of your points**);
return false;
}
i made custom Image View, where a simple line is drawn. This drawing is triggeed by a button.
When triggered the onClicklistener isnt working anymore. But on the screen the button remains clickable, the buttons changes to a blueish color when clicked (default Android button). I think the problem might be in the "drawLine()" when the "setContentView(R.layout.activity_main);" is called, but not sure why and how to get rid of it. Hope you can help.
MainActivity.java
public class MainActivity extends ActionBarActivity implements OnTouchListener,
OnClickListener {
private int number;
private Handler handler;
private boolean Running = true;
private int endX = 50;
private int endY = 500;
private int startX = 50;
private int startY = 50;
private int frames = 25;
ImageView Line01;
Button buttonLineDrawer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Line01 = (ImageView) findViewById(R.id.Line01);
Line01.setVisibility(View.GONE);
Button buttonLineDrawer;
buttonLineDrawer = (Button) findViewById(R.id.buttonLineDrawer);
buttonLineDrawer.setOnClickListener(this);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return false;
}
#Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "klick", Toast.LENGTH_LONG)
.show();
Running = true;
number = 0;
drawLine();
}
public void drawLine() {
Line01.setVisibility(View.VISIBLE);
handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
while (Running) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
#Override
public void run() {
int coordX = ((((endX - startX) / frames) * number))
+ startX;
int coordY = ((((endY - startY) / frames) * number))
+ startY;
number += 1;
CustomDraw.setCoordinates(startX, startY, coordX,
coordY);
setContentView(R.layout.activity_main);
if ((coordX - endX) == 0 && coordY - endY == 0) {
Running = false;
}
}
});
}
}
};
new Thread(runnable).start();
}}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF8800"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="de.trialar.linedrawer.MainActivity" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<Button
android:id="#+id/buttonLineDrawer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
<de.trialar.linedrawer.CustomDraw
android:id="#+id/Line01"
android:layout_width="400dp"
android:layout_height="400dp" />
</LinearLayout></RelativeLayout>
CustomDraw.java
public class CustomDraw extends ImageView {
Paint paint = new Paint();
private static int endX= 500;
private static int endY= 500;
private static int startX= 50;
private static int startY= 50;
static Context context;
public CustomDraw(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
}
public CustomDraw(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomDraw(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.BLACK);
paint.setStrokeWidth(10);
canvas.drawLine(startX, startY, endX, endY, paint);
}
public static void setCoordinates(int startX, int startY, int endX, int endY) {
System.out.println("SetCoordinates");
CustomDraw.endX = endX;
CustomDraw.endY = endY;
CustomDraw.startX = startX;
CustomDraw.startY = startY;
}}
i think i found the problem: when you start the handler.run() you set a new Layout:
public void run() {
int coordX = ((((endX - startX) / frames) * number)) + startX;
int coordY = ((((endY - startY) / frames) * number)) + startY;
number += 1;
CustomDraw.setCoordinates(startX, startY, coordX, coordY);
//maybe this is wrong
setContentView(R.layout.activity_main);
//try instead:
CustomDraw.invalidate();
if ((coordX - endX) == 0 && coordY - endY == 0) {
Running = false;
}
}
i don't get why you set a new layout... i guess you want to repaint the button... that's why i added a 'repaint'-line in that code-piece above
I searched the topic for several days, but finally confused. The idea is to create own alarm app with some special tricks. Firstly, I need clock hands. For creating custom clock, I used own class, which extends View. Hour and minute hands are PNG images.
They should be located in the center of the screen, but they dont. Actually, I can't even see them. And that is the question.
Here is the Clock class
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
//import android.os.Handler;
import android.text.format.Time;
import android.util.AttributeSet;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
#SuppressLint("NewApi")
public class Clock extends View {
public Clock(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
private Drawable mHourHand;
private Drawable mMinuteHand;
private boolean mAttached;
static private float mMinutes;
static private float mHour;
private boolean mChanged;
Context mContext;
private boolean mSeconds;
// Gettes & setters. These clock must present alarm time which user sets in the next view
protected float getmMinutes() {
return mMinutes;
}
protected static void setmMinutes(float mMinutes) {
Clock.mMinutes = mMinutes;
}
protected float getmHour() {
return mHour;
}
protected static void setmHour(float mHour) {
Clock.mHour = mHour;
}
private Point size;
// ctors
public Clock(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public Clock(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
Resources r = context.getResources();
TypedArray a =
context.obtainStyledAttributes(attrs, R.styleable.AnalogClock, defStyle, 0);
mContext=context;
mHourHand = r.getDrawable(R.drawable.hours);
mMinuteHand = r.getDrawable(R.drawable.minuts);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (!mAttached) {
mAttached = true;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
// getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int desiredWidth = 150; // and yes, 150 what? px, inches, dpi-s? I draw it just randomly
int desiredHeight = 150;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
//Measure Width
if (widthMode == MeasureSpec.EXACTLY) {
//Must be this size
width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
width = Math.min(desiredWidth, widthSize);
} else {
//Be whatever you want
width = desiredWidth;
}
//Measure Height
if (heightMode == MeasureSpec.EXACTLY) {
//Must be this size
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
height = Math.min(desiredHeight, heightSize);
} else {
//Be whatever you want
height = desiredHeight;
}
setMeasuredDimension(width, height);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mChanged = true;
}
#SuppressWarnings({ "deprecation" })
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
boolean changed = mChanged;
if (changed) {
mChanged = false;
}
boolean seconds = mSeconds;
if (seconds ) {
mSeconds = false;
}
int w = 100; //These are too made randomly
int h = 100;
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
// if (android.os.Build.VERSION.SDK_INT <= 13)
// {
w = display.getWidth(); // deprecated
h = display.getHeight(); // deprecated
//}
// else if (android.os.Build.VERSION.SDK_INT > 13)
//{
//size = null;
//display.getSize(size);
//w = size.x;
//h = size.y;
//} ... I cant figure out, why, but size returns null. So I'll use deprecated ones just for
now.
// **Here are my measures. I suggest that if height of an hour hand should be about 1/4 of
screen width, then following the proportion - width of that hand should be old width*new
height/old height, or smthng**
int sizeXHour = w/3;
int sizeYHour = mHourHand.getIntrinsicHeight()*sizeXHour/mHourHand.getIntrinsicWidth();
int xHour = sizeXHour/2;
int yHour = sizeYHour/2;
canvas.rotate(mHour / 12.0f * 360.0f, xHour, yHour);
final Drawable hourHand = mHourHand;
if (changed) {
hourHand.setBounds((w / 2) - xHour, (h / 2) - yHour, sizeXHour, sizeYHour);
}
hourHand.draw(canvas);
canvas.restore();
int sizeYMinute = h/4;
int sizeXMinute = mMinuteHand.getIntrinsicWidth()*sizeYMinute/mMinuteHand.getIntrinsicHeight();
int xMinute = sizeXMinute/2;
int yMinute = sizeYMinute/2;
canvas.save();
canvas.rotate(mMinutes / 60.0f * 360.0f, xMinute, yMinute);
final Drawable minuteHand = mMinuteHand;
if (changed) {
minuteHand.setBounds((w / 2 - xMinute), (h / 2 - yMinute), sizeXMinute, sizeYMinute);
}
minuteHand.draw(canvas);
canvas.restore();
canvas.save();
}
I feel that something is very wrong, but can not figure what. The XML is:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="ee.st.running.dreamyclock.MainActivity"
android:background = "#drawable/clockk" >
<View
android:id="#+id/view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<st.running.dreamyclock.Clock
android:id="#+id/clock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical = "true"
android:layout_marginTop="124dp" />
</RelativeLayout>
the attrs are:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="AnalogClock">
<attr name="hand_hour" format="reference"/>
<attr name="hand_minute" format="reference"/>
</declare-styleable>
</resources>
Any ideas where it came from? Thank you
Was this ever working? Try to add this to Clock constructor:
setWillNotDraw(false);
"If this view doesn't do any drawing on its own, set this flag to allow further optimizations. By default, this flag is not set on View, but could be set on some View subclasses such as ViewGroup. Typically, if you override onDraw(android.graphics.Canvas) you should clear this flag."
Edit:
If you want to re draw your view, you should call invalidate() method
"Invalidate the whole view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future. "
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mChanged = true;
this.invalidate();
}
You don't even have to call invalidate(); if you just want to for example rotate the view:
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mChanged = true;
setRotation(20);
}
Edit 2:
When you are setting bounds for a Drawable, the "right" value should be higher than "left" for example:
minuteHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2) + 4, y + (h / 4));
or
minuteHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2) + minuteHand.getIntrinsicWidth(), y + (h / 4));
I want the text inside my TextView to fit the screen. I need to implement something like this:
The letter 'A' needs to be at the center of the screen with height equal to the height of the device screen. I have created a Custom TextView for this as follows but that doesn't seem to work. What I mean by this is that my text (letter A) isn't fitting the height of the screen. I tried manually adjusting the text font size but that isn't the right way I guess. Can someone point out a better solution for this?
package com.example;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
public class FontFitTextView extends TextView
{
private Paint mTestPaint;
private float maxFontSize;
private static final float MAX_FONT_SIZE_DEFAULT_VALUE = 20f;
public FontFitTextView(Context context)
{
super(context);
initialise(context, null);
}
public FontFitTextView(Context context, AttributeSet attributeSet)
{
super(context, attributeSet);
initialise(context, attributeSet);
}
public FontFitTextView(Context context, AttributeSet attributeSet, int defStyle)
{
super(context, attributeSet, defStyle);
initialise(context, attributeSet);
}
private void initialise(Context context, AttributeSet attributeSet)
{
if(attributeSet!=null)
{
TypedArray styledAttributes = context.obtainStyledAttributes(attributeSet, R.styleable.FontFitTextView);
maxFontSize = styledAttributes.getDimension(R.styleable.FontFitTextView_maxFontSize, MAX_FONT_SIZE_DEFAULT_VALUE);
styledAttributes.recycle();
}
else
{
maxFontSize = MAX_FONT_SIZE_DEFAULT_VALUE;
}
mTestPaint = new Paint();
mTestPaint.set(this.getPaint());
//max size defaults to the initially specified text size unless it is too small
}
private void refitText(String text, int textWidth, int textHeight)
{
if (textWidth <= 0)
return;
int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight();
int targetHeight = textHeight - this.getPaddingTop() - this.getPaddingBottom();
float hi = maxFontSize;
float lo = 2;
final float threshold = 1f; // How close we have to be
mTestPaint.set(this.getPaint());
Rect bounds = new Rect();
while ((hi - lo) > threshold)
{
float size = (hi + lo) / 2;
mTestPaint.setTextSize(size);
mTestPaint.getTextBounds(text, 0, text.length(), bounds);
if (bounds.width() >= targetWidth || bounds.height() >= targetHeight)
hi = size; // too big
else
lo = size; // too small
}
// Use lo so that we undershoot rather than overshoot
this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int height = getMeasuredHeight();
refitText(this.getText().toString(), parentWidth, height);
this.setMeasuredDimension(parentWidth, height);
}
#Override
protected void onTextChanged(final CharSequence text, final int start, final int before, final int after)
{
refitText(text.toString(), this.getWidth(), this.getHeight());
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
if (w != oldw)
{
refitText(this.getText().toString(), w, h);
}
}
}
XML file
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:res-auto="http://schemas.android.com/apk/res-auto"
android:id="#+id/home_Layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:id="#+id/linear1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical">
<com.example.FontFitTextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="1"
android:textSize="80sp"
res-auto:maxFontSize="55sp" />
</LinearLayout>
</RelativeLayout>
Try calling super.onMeasure() method at the end of the onMeasure() method with the updated width and height (parentWidth, height).
I'm trying to implement a personal way of undo/redo in a finger paint-like app.
I have in synthesis three objects: the Main class (named ScorePadActivity), the relative Main Layout (with buttons, menus, etc, as well as a View object where I create my drawings), and a third object named ArrayList where i'm writing the undo/redo code.
The problem is, when I press the undo button nothing happens, but if I draw anything again "one-time" and press undo, the screen is updated. If I draw many times, to see any changes happen on screen I have to press the undo button the same number of times I have drawn.
Seems like (as in title) when I add a bitmap to the array list the last element is duplicated in previous indexes, and for some strange reason, everytime I press the Undo Button, the system is ok for one time, but starts to duplicate until the next undo.
The index increase is verified with a series of System.out.println inserted in code.
Now when I draw something on screen, the array list is updated with the code inserted after the invocation of touchup(); method in motionEvent
touch_up(); }
this.arrayClass.incrementArray(mBitmap);
mPath.rewind();
invalidate();
and in ArrayList activity;
public void incrementArray(Bitmap mBitmap) {
this._mBitmap=mBitmap;
_size=undoArray.size();
undoArray.add(_size, _mBitmap);
}
(All Logs removed for clear reading)
The undo button in ScorePadActivity calls the undo method in View activity:
Button undobtn= (Button)findViewById(R.id.undo);
undobtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mView.undo();
}
});
in View activity:
public void undo() {
this.mBitmap= arrayClass.undo();
mCanvas = new Canvas(mBitmap);
mPath.rewind();
invalidate();
}
that calls the relative undo method in ArrayList activity:
public Bitmap undo() {
// TODO Auto-generated method stub
_size=undoArray.size();
if (_size>1) {
undoArray.remove(_size-1);
_size=undoArray.size();
_mBitmap = ((Bitmap) undoArray.get(_size-1)).copy(Bitmap.Config.ARGB_8888,true);
}
return _mBitmap;
}
return mBitmap and invalidate:
Due to my bad English I have made a scheme to make the problem more clear:
I have tried with HashMap, with a simple array, I have tried to change mPath.rewind(); with reset();, new Path(); etc but nothing.
Why?
Sorry for the complex answer, i want give you a great thanks in advance.
Best regards
Edit
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:id="#+id/buttonlayout"
android:layout_width="fill_parent"
android:layout_height="50dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
some other layouts nested an buttons to form a upper toolbar
</LinearLayout>
<RelativeLayout
android:id="#+id/viewlayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#+id/buttonlayout"
android:background="#drawable/desk_wood" >
<com.example.android.touchexample.MyView
android:id="#+id/viewout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />
</RelativeLayout>
</RelativeLayout>
This the Main Activity ScorePadActivity
public class ScorePadActivity extends Activity {
MyView mView;
public void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mView = (MyView)findViewById(R.id.viewout);
Button undobtn= (Button)findViewById(R.id.undo);
undobtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mView.undo();
}
});
This is the View Activity:
public class MyView extends View {
MyView myView;
Context context;
final ArrayClass arrayClass= new ArrayClass();
private Bitmap mBitmap;
private Bitmap savedBmp;
private static Canvas mCanvas;
private static Path mPath;
private static Paint mPaint;
/*
* some other variables here
*/
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(color);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(bSize);
mPaint.setAlpha(255);
mPath = new Path();
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap((int) bmWidth, (int) bmHeight,Bitmap. Config.ARGB_8888);}
/*
* here add a blank bitmap at the start of the array at index 0
*/
arrayClass.incrementArray(mBitmap);
mCanvas = new Canvas(mBitmap);
}
public void onDraw(Canvas canvas) {
canvas.save();
bx= (((width/mScaleFactor)-width)/2)+center;
by= ((height/mScaleFactor)-height)/2;
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
canvas.drawBitmap(penta, bx, by, null);
mCanvas.drawPath(mPath, mPaint);
canvas.drawBitmap(mBitmap, bx, by, null);
lastmPosX=mPosX;
lastmPosY=mPosY;
lastmScaleFactor=mScaleFactor;
canvas.restore();
}
private void touch_start(float x, float y) {
x=((x/mScaleFactor)-bx)-(mPosX/mScaleFactor);
y=((y/mScaleFactor)-by)-(mPosY/mScaleFactor);
mPath.rewind();
mPath.moveTo(x, y);
move=false;
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
x=((x/mScaleFactor)-bx)-(mPosX/mScaleFactor);
y=((y/mScaleFactor)-by)-(mPosY/mScaleFactor);
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
move=true;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// mPath.rewind();
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
x = ev.getX();
y = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
// Here update the arraylist in the ArrayList activity
this.arrayClass.incrementArray(mBitmap);
mPath.rewind();
invalidate();
break;}
return true;
}
/*
* more methods here
*/ switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
public void undo() {
// Here recall the last mBitmap from Arraylist activity
this.mBitmap= arrayClass.undo();
mCanvas = new Canvas(mBitmap);
mPath.rewind();
invalidate();
}
}
and this is my ArrayList activity:
public class ArrayClass {
ArrayList<Bitmap> undoArray =new ArrayList<Bitmap>();
private int _size;
private Bitmap _mBitmap;
public void incrementArray(Bitmap mBitmap) {
this._mBitmap=mBitmap;
_size=undoArray.size();
// undoArray.add(_size, _mBitmap);
undoArray.add(_size, Bitmap.createBitmap(_mBitmap));
}
public Bitmap undo() {
// TODO Auto-generated method stub
_size=undoArray.size();
if (_size>1) {
undoArray.remove(_size-1);
_size=undoArray.size();
_mBitmap = ((Bitmap) undoArray.get(_size-1)).copy(Bitmap.Config.ARGB_8888,true);
}
return _mBitmap;
}
public Bitmap redo() {
// TODO
return null;
}
}
Thanks again
Here the solution, edit the ArrayClass in this way:
public class ArrayClass {
ArrayList<Bitmap> undoArray =new ArrayList<Bitmap>();
private int _size;
private Bitmap _mBitmap;
public void incrementArray(Bitmap mBitmap) {
this._mBitmap=mBitmap;
_size=undoArray.size();
// undoArray.add(_size, _mBitmap); replace with:
undoArray.add(_size, Bitmap.createBitmap(_mBitmap));
}
public Bitmap undo() {
// TODO Auto-generated method stub
_ size=undoArray.size();
if (_size>1) {
undoArray.remove(_size-1);
_size=undoArray.size();
_mBitmap = ((Bitmap) undoArray.get(_size-1)).copy(Bitmap.Config.ARGB_8888,true);
}
return _mBitmap;
}
public Bitmap redo() {
// TODO
return null;
}
}