I was developing user "eluleci" (https://gist.github.com/eluleci/6e0d02c766b27f6a5253) code
it's rippleTuch for preLilop
ok , it's extend Button class for do it,
when we have one button in our layout, all things work good and animation work smooth,
but when we add more than one button , like 5 button, by touch them the animation on the buttons get lag and not smooth!!
I cant understand what's the problem? Memory issues? and how can I fix it?can anyone knew where is the problem?
thank you for your attention
here is my code:
public class MehDiRippleButton extends Button {
#Override
public boolean isInEditMode() {
return true;
}
Paint paint=new Paint();
private static final int AnimDuration = 9000;
private TouchEffectAnimator touchEffectAnimator;
public MehDiRippleButton(Context context) {
super(context);
init();
}
public MehDiRippleButton(Context context, AttributeSet attrs) {
super(context, attrs);
allocated_TuchEffectAnimator();
TypedArray typedArray_MehDi_rippleButton_style = context.obtainStyledAttributes(attrs,R.styleable.MehDi_rippleButton_style);
CharSequence charSequence_buttonColor =typedArray_MehDi_rippleButton_style.getString(R.styleable.MehDi_rippleButton_style_MehDi_buttonColor);
paint.setColor(Color.parseColor(charSequence_buttonColor.toString()));
touchEffectAnimator.setStroke(paint);
CharSequence charSequence_rippleColor =typedArray_MehDi_rippleButton_style.getString(R.styleable.MehDi_rippleButton_style_MehDi_rippleColor);
if(charSequence_rippleColor!=null) {
touchEffectAnimator.setEffectColor(Color.parseColor(charSequence_rippleColor.toString()),Color.parseColor(charSequence_buttonColor.toString()));
}else touchEffectAnimator.setEffectColor(Color.LTGRAY,Color.WHITE);
typedArray_MehDi_rippleButton_style.recycle();
init();
}
private void allocated_TuchEffectAnimator(){
touchEffectAnimator = new TouchEffectAnimator(this);
}
private void init() {
touchEffectAnimator.setHasRippleEffect(true);
touchEffectAnimator.setAnimDuration(AnimDuration);
touchEffectAnimator.setClipRadius(0);
setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
}
});
}
#Override
public boolean onTouchEvent(final MotionEvent event) {
touchEffectAnimator.onTouchEvent(event);
return super.onTouchEvent(event);
}
#Override
protected void onDraw(Canvas canvas) {
touchEffectAnimator.onDraw(canvas);
super.onDraw(canvas);
}
}
and :
(in this class occurred the problem):
public class TouchEffectAnimator {
float u=0;
private static final int fadeout_time_helper=29;
private static final int tuchUp_time_helper=14;
private final int EASE_ANIM_DURATION = 2000;
private final int RIPPLE_ANIM_DURATION = 3000;
private final int MAX_RIPPLE_ALPHA = 255;
private View mView;
private int mClipRadius;
private boolean hasRippleEffect = false;
private int animDuration = EASE_ANIM_DURATION;
private int requiredRadius;
private float mDownX;
private float mDownY;
private float mRadius;
private int mCircleAlpha = MAX_RIPPLE_ALPHA;
private int mRectAlpha = 0;
private Paint mCirclePaint = new Paint();
private Paint mStrokePaint = new Paint();
private Paint mRectPaint = new Paint();
private Path mCirclePath = new Path();
private Path mRectPath = new Path();
private boolean isTouchReleased = false;
private boolean isAnimatingFadeIn = false;
/**
*
*
* */
private Animation.AnimationListener animationListener = new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
isAnimatingFadeIn = true;
}
#Override
public void onAnimationEnd(Animation animation) {
isAnimatingFadeIn = false;
if (isTouchReleased){
setStroke(mStrokePaint);
fadeOutEffect();
}
}
#Override
public void onAnimationRepeat(Animation animation) {
}
};
public TouchEffectAnimator(View mView) {
this.mView = mView;
}
public void setHasRippleEffect(boolean hasRippleEffect) {
this.hasRippleEffect = hasRippleEffect;
if (hasRippleEffect) animDuration = RIPPLE_ANIM_DURATION;
}
public void setAnimDuration(int animDuration) {
this.animDuration = animDuration;
}
public void setEffectColor(int effectColor,int buttonColor) {
mCirclePaint.setColor(effectColor);
mCirclePaint.setAlpha(mCircleAlpha);
mRectPaint.setColor(effectColor);
mRectPaint.setAlpha(mRectAlpha);
mStrokePaint.setColor(buttonColor);
}
public void setClipRadius(int mClipRadius) {
this.mClipRadius = mClipRadius;
}
public void onTouchEvent(final MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
isTouchReleased = true;
Drawable background = mView.getBackground();
if (background instanceof ShapeDrawable) {
((ShapeDrawable)background).getPaint().setStrokeWidth(0);
Log.e("fdsfsfd","fdsfdsfdsf");
} else if (background instanceof GradientDrawable) {
//((GradientDrawable)background).setS
Log.e("qqq","qqq");
}
ValueGeneratorAnim valueGeneratorAnim = new ValueGeneratorAnim(new InterpolatedTimeCallback() {
#Override
public void onTimeUpdate(float interpolatedTime) {
if (hasRippleEffect)
mRadius = requiredRadius * interpolatedTime +u;
mRectAlpha = (int) (interpolatedTime * MAX_RIPPLE_ALPHA);
mView.invalidate();
}
});
valueGeneratorAnim.setInterpolator(new DecelerateInterpolator());
valueGeneratorAnim.setDuration(animDuration/tuchUp_time_helper);
valueGeneratorAnim.setAnimationListener(animationListener);
mView.startAnimation(valueGeneratorAnim);
if (!isAnimatingFadeIn) {
setStroke(mStrokePaint);
fadeOutEffect();
}
} else if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
// gets the bigger value (width or height) to fit the circle
requiredRadius = mView.getWidth() >= mView.getHeight() ? mView.getWidth() : mView.getHeight();
final int requiredRadius2 = mView.getWidth() < mView.getHeight() ? mView.getWidth() : mView.getHeight();
noStroke(mStrokePaint);
requiredRadius *= 1.2;
Log.e("req ", "" + requiredRadius);
isTouchReleased = false;
mDownX = event.getX();
mDownY = mView.getMeasuredHeight()/2;
Drawable background = mView.getBackground();
if (background instanceof ShapeDrawable) {
((ShapeDrawable)background).getPaint().setStrokeWidth(0);
Log.e("fdsfsfd","fdsfdsfdsf");
} else if (background instanceof GradientDrawable) {
//((GradientDrawable)background).setS
Log.e("qqq","qqq");
}
mCircleAlpha = MAX_RIPPLE_ALPHA;
mRectAlpha = 0;
ValueGeneratorAnim valueGeneratorAnim = new ValueGeneratorAnim(new InterpolatedTimeCallback() {
#Override
public void onTimeUpdate(float interpolatedTime) {
Log.e("1 ", "" + 1);
if (hasRippleEffect)
mRadius = requiredRadius * interpolatedTime+(float)(requiredRadius2/1.5);
u=mRadius;
Log.e("mRa ", "" + mRadius);
Log.e("int ", "" + interpolatedTime);
mRectAlpha = (int) (interpolatedTime * MAX_RIPPLE_ALPHA);
mView.invalidate();
}
});
Log.e("222 ", "" + 222);
valueGeneratorAnim.setInterpolator(new DecelerateInterpolator());
valueGeneratorAnim.setDuration(animDuration);
valueGeneratorAnim.setAnimationListener(animationListener);
mView.startAnimation(valueGeneratorAnim);
}
}
public void onDraw(final Canvas canvas) {
if (hasRippleEffect) {
mCirclePath.reset();
mCirclePaint.setAlpha(mCircleAlpha);
mCirclePath.addRoundRect(new RectF(0, 0, mView.getWidth(), mView.getHeight()),
mClipRadius, mClipRadius, Path.Direction.CW);
canvas.clipPath(mCirclePath);
canvas.drawCircle(mDownX, mDownY, mRadius, mCirclePaint);
}
mRectPath.reset();
if (hasRippleEffect && mCircleAlpha != 255) mRectAlpha = mCircleAlpha / 2;
mRectPaint.setAlpha(mRectAlpha);
canvas.drawRoundRect(new RectF(0, 0, mView.getWidth(), mView.getHeight()), mClipRadius,mClipRadius, mRectPaint);
}
private void fadeOutEffect() {
ValueGeneratorAnim valueGeneratorAnim = new ValueGeneratorAnim(new InterpolatedTimeCallback() {
#Override
public void onTimeUpdate(float interpolatedTime) {
mCircleAlpha = (int) (MAX_RIPPLE_ALPHA - (MAX_RIPPLE_ALPHA * interpolatedTime));
mRectAlpha = mCircleAlpha;
mView.invalidate();
}
});
valueGeneratorAnim.setDuration(animDuration / fadeout_time_helper); /**change anim fade out time*/
mView.startAnimation(valueGeneratorAnim);
}
class ValueGeneratorAnim extends Animation {
private InterpolatedTimeCallback interpolatedTimeCallback;
ValueGeneratorAnim(InterpolatedTimeCallback interpolatedTimeCallback) {
this.interpolatedTimeCallback = interpolatedTimeCallback;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
this.interpolatedTimeCallback.onTimeUpdate(interpolatedTime);
}
}
interface InterpolatedTimeCallback {
public void onTimeUpdate(float interpolatedTime);
}
public void setStroke(Paint mStrokePaint){
GradientDrawable gradiant_withStroke = new GradientDrawable();
gradiant_withStroke.setColor(mStrokePaint.getColor());
gradiant_withStroke.setStroke(8, Color.parseColor("#00000000"));
mView.setBackgroundDrawable(gradiant_withStroke);
}
public void noStroke(Paint mStrokePaint){
GradientDrawable gradiant_withStroke = new GradientDrawable();
gradiant_withStroke.setColor(mStrokePaint.getColor());
gradiant_withStroke.setStroke(0, Color.parseColor("#00000000"));
mView.setBackgroundDrawable(gradiant_withStroke);
}
}
and 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"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical">
<com.example.mahdi.myapplication.MehDiRippleButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/view2"
android:layout_gravity="center_horizontal"
android:text="Ripple Tuch"
app:MehDi_rippleColor="#cf030a"
app:MehDi_buttonColor="#ff2c25"
/>
<com.example.mahdi.myapplication.MehDiRippleButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/view34"
android:text="Produce By"
app:MehDi_rippleColor="#f7f336"
app:MehDi_buttonColor="#d3ce00"
/>
<com.example.mahdi.myapplication.MehDiRippleButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/view"
android:layout_gravity="center_horizontal"
android:text="MehDi"
app:MehDi_rippleColor="#00930e"
app:MehDi_buttonColor="#00b909"
/>
<com.example.mahdi.myapplication.MehDiRippleButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/view3"
android:layout_gravity="center_horizontal"
android:text="NazaRi"
app:MehDi_rippleColor="#303dd3"
app:MehDi_buttonColor="#7688e5"
/>
<com.example.mahdi.myapplication.MehDiRippleButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/view23"
android:layout_gravity="center_horizontal"
android:text="؛)"
app:MehDi_rippleColor="#cf0584"
app:MehDi_buttonColor="#ff0fb6"
/>
<com.example.mahdi.myapplication.MehDiRippleButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/viehw23"
android:layout_gravity="center_horizontal"
android:text=":D"
app:MehDi_rippleColor="#a2a3a2"
app:MehDi_buttonColor="#dadbda"
/>
You need to dig out the Handler which executes most of these animation calls, and invoke handler.removeCallbacksAndMessages(null); and that will take care of some of the lag.
Because most probably, when you finish and reenter the said activity, it works good and fast as when first ran. This can be caused by having alot of Threads or Handler messages that have ended their lifecycle but haven't been removed due to being dead.
Related
I've searched, but haven't found anything I'm looking for, perhaps. I want load image into my custom drawing view. Here is the code of DrawingView that extend View.
private Path mDrawPath;
private Paint mBackgroundPaint;
private Paint mDrawPaint;
private Canvas mDrawCanvas;
private Bitmap mCanvasBitmap;
private ArrayList<Path> mPaths = new ArrayList<>();
private ArrayList<Paint> mPaints = new ArrayList<>();
private ArrayList<Path> mUndonePaths = new ArrayList<>();
private ArrayList<Paint> mUndonePaints = new ArrayList<>();
// Set default values
private int mBackgroundColor = 0xFFFFFFFF;
private int mPaintColor = 0xFF660000;
private int mStrokeWidth = 10;
public DrawingView(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
private void init()
{
mDrawPath = new Path();
mBackgroundPaint = new Paint();
initPaint();
}
private void initPaint()
{
mDrawPaint = new Paint();
mDrawPaint.setColor(mPaintColor);
mDrawPaint.setAntiAlias(true);
mDrawPaint.setStrokeWidth(mStrokeWidth);
mDrawPaint.setStyle(Paint.Style.STROKE);
mDrawPaint.setStrokeJoin(Paint.Join.ROUND);
mDrawPaint.setStrokeCap(Paint.Cap.ROUND);
}
private void drawBackground(Canvas canvas)
{
mBackgroundPaint.setColor(Color.TRANSPARENT);
mBackgroundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
mBackgroundPaint.setAntiAlias(true);
canvas.drawRect(0, 0, this.getWidth(), this.getHeight(), mBackgroundPaint);
}
private void drawPaths(Canvas canvas)
{
int i = 0;
for (Path p : mPaths)
{
canvas.drawPath(p, mPaints.get(i));
i++;
}
}
#Override
protected void onDraw(Canvas canvas)
{
drawBackground(canvas);
drawPaths(canvas);
canvas.drawPath(mDrawPath, mDrawPaint);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
mCanvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mDrawCanvas = new Canvas(mCanvasBitmap);
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
mDrawPath.moveTo(touchX, touchY);
//mDrawPath.addCircle(touchX, touchY, mStrokeWidth/10, Path.Direction.CW);
break;
case MotionEvent.ACTION_MOVE:
mDrawPath.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
mDrawPath.lineTo(touchX, touchY);
mPaths.add(mDrawPath);
mPaints.add(mDrawPaint);
mDrawPath = new Path();
initPaint();
break;
default:
return false;
}
invalidate();
return true;
}
public void clearCanvas()
{
mPaths.clear();
mPaints.clear();
mUndonePaths.clear();
mUndonePaints.clear();
mDrawCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
invalidate();
}
public void setPaintColor(int color)
{
mPaintColor = color;
mDrawPaint.setColor(mPaintColor);
}
public void setPaintStrokeWidth(int strokeWidth)
{
mStrokeWidth = strokeWidth;
mDrawPaint.setStrokeWidth(mStrokeWidth);
}
public void setBackgroundColor(int color)
{
mBackgroundColor = color;
mBackgroundPaint.setColor(mBackgroundColor);
invalidate();
}
public Bitmap getBitmap()
{
drawBackground(mDrawCanvas);
drawPaths(mDrawCanvas);
return mCanvasBitmap;
}
public void undo()
{
if (mPaths.size() > 0)
{
mUndonePaths.add(mPaths.remove(mPaths.size() - 1));
mUndonePaints.add(mPaints.remove(mPaints.size() - 1));
invalidate();
}
}
public void redo()
{
if (mUndonePaths.size() > 0)
{
mPaths.add(mUndonePaths.remove(mUndonePaths.size() - 1));
mPaints.add(mUndonePaints.remove(mUndonePaints.size() - 1));
invalidate();
}
}
BoardActivity class code is:
private TextView titolo;
private DrawingView areaDisegno;
private ImageView sceltaColore, sceltaSpessore, undo, redo;
private Bitmap drawable;
private int mCurrentBackgroundColor;
private int mCurrentColor;
private int mCurrentStroke;
private static final int MAX_STROKE_WIDTH = 50;
#Override
protected void onCreate(Bundle savedInstanceState) {
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setNavigationBarColor(getResources().getColor(R.color.black));
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_board1);
titolo = findViewById(R.id.titolo);
titolo.setText("MATCHUP BOARD 1");
areaDisegno = findViewById(R.id.area_disegno);
sceltaColore= findViewById(R.id.scelta_colore);
sceltaSpessore = findViewById(R.id.scelta_spessore);
undo = findViewById(R.id.undo);
redo = findViewById(R.id.redo);
sceltaColore.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startColorPickerDialog();
}
});
sceltaSpessore.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startStrokeSelectorDialog();
}
});
undo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
areaDisegno.undo();
}
});
redo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
areaDisegno.redo();;
}
});
inizializzaAreaDisegno();
}
private void inizializzaAreaDisegno()
{
// mCurrentBackgroundColor = ContextCompat.getColor(this, android.R.color.white);
mCurrentColor = ContextCompat.getColor(this, android.R.color.black);
mCurrentStroke = 10;
areaDisegno.setBackgroundColor(mCurrentBackgroundColor);
areaDisegno.setPaintColor(mCurrentColor);
areaDisegno.setPaintStrokeWidth(mCurrentStroke);
}
private void startFillBackgroundDialog()
{
int[] colors = getResources().getIntArray(R.array.palette);
ColorPickerDialog dialog = ColorPickerDialog.newInstance(R.string.color_picker_default_title,
colors,
mCurrentBackgroundColor,
5,
ColorPickerDialog.SIZE_SMALL);
dialog.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
{
#Override
public void onColorSelected(int color)
{
mCurrentBackgroundColor = color;
areaDisegno.setBackgroundColor(mCurrentBackgroundColor);
}
});
dialog.show(getFragmentManager(), "ColorPickerDialog");
}
private void startColorPickerDialog()
{
int[] colors = getResources().getIntArray(R.array.palette);
ColorPickerDialog dialog = ColorPickerDialog.newInstance(R.string.color_picker_default_title,
colors,
mCurrentColor,
5,
ColorPickerDialog.SIZE_SMALL);
dialog.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
{
#Override
public void onColorSelected(int color)
{
mCurrentColor = color;
areaDisegno.setPaintColor(mCurrentColor);
}
});
dialog.show(getFragmentManager(), "ColorPickerDialog");
}
private void startStrokeSelectorDialog()
{
StrokeSelectorDialog dialog = StrokeSelectorDialog.newInstance(mCurrentStroke, MAX_STROKE_WIDTH);
dialog.setOnStrokeSelectedListener(new StrokeSelectorDialog.OnStrokeSelectedListener()
{
#Override
public void onStrokeSelected(int stroke)
{
mCurrentStroke = stroke;
areaDisegno.setPaintStrokeWidth(mCurrentStroke);
}
});
dialog.show(getSupportFragmentManager(), "StrokeSelectorDialog");
}
private void startShareDialog(Uri uri)
{
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("image/*");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "");
intent.putExtra(android.content.Intent.EXTRA_TEXT, "");
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intent, "Share Image"));
}
private void requestPermissionsAndSaveBitmap()
{
if (PermissionManager.checkWriteStoragePermissions(this))
{
Uri uri = FileManager.saveBitmap(this, areaDisegno.getBitmap());
startShareDialog(uri);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode)
{
case PermissionManager.REQUEST_WRITE_STORAGE:
{
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
{
Uri uri = FileManager.saveBitmap(this, areaDisegno.getBitmap());
startShareDialog(uri);
} else
{
Toast.makeText(this, "The app was not allowed to write to your storage. Hence, it cannot function properly. Please consider granting it this permission", Toast.LENGTH_LONG).show();
}
}
}
}
And Layout used is:
<?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="#drawable/sfondo"
tools:context=".activities.Board1">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusableInTouchMode="true">
<include
android:id="#+id/toolbar"
layout="#layout/toolbar_menu_disegno"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/toolbar"
android:orientation="vertical"
android:weightSum="2"
>
<FrameLayout
android:layout_weight="1.99"
android:layout_width="match_parent"
android:layout_height="0dp" >
<it.colan.matchup.ui.component.DrawingView
android:id="#+id/area_disegno"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:rotation="90"
android:src="#drawable/board_1" />
</FrameLayout>
<LinearLayout
android:id="#+id/menu_strumenti"
android:layout_weight="0.01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/area_disegno"
android:orientation="horizontal"
android:gravity="center"
android:background="#color/black">
<ImageView
android:id="#+id/scelta_colore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_palette"
android:padding="15dp"
android:clickable="true"
android:background="?attr/selectableItemBackgroundBorderless"/>
<ImageView
android:id="#+id/scelta_spessore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_peso_linea"
android:padding="15dp"
android:clickable="true"
android:background="?attr/selectableItemBackgroundBorderless"/>
<ImageView
android:id="#+id/undo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_undo"
android:padding="15dp"
android:clickable="true"
android:background="?attr/selectableItemBackgroundBorderless"/>
<ImageView
android:id="#+id/redo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_redo"
android:padding="15dp"
android:clickable="true"
android:background="?attr/selectableItemBackgroundBorderless"/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
I have two problems:
The drawing does it for me behind the image and not above
imagebug
The background of the image is not full screen with margins.
How should I do?
Thanks
Cris
I am making a custom edittext to enter mobile numbers. Here is the code
public class PinEntryEditText extends android.support.v7.widget.AppCompatEditText {
private static final String XML_NAMESPACE_ANDROID = "http://schemas.android.com/apk/res/android";
protected String mMask = null;
protected StringBuilder mMaskChars = null;
protected String mSingleCharHint = null;
protected int mAnimatedType = 0;
protected float mSpace = 24; //24 dp by default, space between the lines
protected float mCharSize;
protected float mNumChars = 4;
protected float mTextBottomPadding = 8; //8dp by default, height of the text from our lines
protected int mMaxLength = 4;
protected RectF[] mLineCoords;
protected float[] mCharBottom;
protected Paint mCharPaint;
protected Paint mLastCharPaint;
protected Paint mSingleCharPaint;
protected Drawable mPinBackground;
protected Rect mTextHeight = new Rect();
protected boolean mIsDigitSquare = false;
protected View.OnClickListener mClickListener;
protected OnPinEnteredListener mOnPinEnteredListener = null;
protected float mLineStroke = 1; //1dp by default
protected float mLineStrokeSelected = 2; //2dp by default
protected Paint mLinesPaint;
protected boolean mAnimate = false;
protected boolean mHasError = false;
protected ColorStateList mOriginalTextColors;
protected int[][] mStates = new int[][]{
new int[]{android.R.attr.state_selected}, // selected
new int[]{android.R.attr.state_active}, // error
new int[]{android.R.attr.state_focused}, // focused
new int[]{-android.R.attr.state_focused}, // unfocused
};
protected int[] mColors = new int[]{
Color.GREEN,
Color.RED,
Color.BLACK,
Color.GRAY
};
protected ColorStateList mColorStates = new ColorStateList(mStates, mColors);
public PinEntryEditText(Context context) {
super(context);
}
public PinEntryEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public PinEntryEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// this(context, attrs, android.R.attr.editTextStyle);
init(context, attrs);
}
public void setMaxLength(final int maxLength) {
mMaxLength = maxLength;
mNumChars = maxLength;
setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
setText(null);
invalidate();
}
private void init(Context context, AttributeSet attrs) {
float multi = context.getResources().getDisplayMetrics().density;
mLineStroke = multi * mLineStroke;
mLineStrokeSelected = multi * mLineStrokeSelected;
mSpace = multi * mSpace; //convert to pixels for our density
mTextBottomPadding = multi * mTextBottomPadding; //convert to pixels for our density
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PinEntryEditText, 0, 0);
try {
TypedValue outValue = new TypedValue();
ta.getValue(R.styleable.PinEntryEditText_pinAnimationType, outValue);
mAnimatedType = outValue.data;
mMask = ta.getString(R.styleable.PinEntryEditText_pinCharacterMask);
mSingleCharHint = ta.getString(R.styleable.PinEntryEditText_pinRepeatedHint);
mLineStroke = ta.getDimension(R.styleable.PinEntryEditText_pinLineStroke, mLineStroke);
mLineStrokeSelected = ta.getDimension(R.styleable.PinEntryEditText_pinLineStrokeSelected, mLineStrokeSelected);
mSpace = ta.getDimension(R.styleable.PinEntryEditText_pinCharacterSpacing, mSpace);
mTextBottomPadding = ta.getDimension(R.styleable.PinEntryEditText_pinTextBottomPadding, mTextBottomPadding);
mIsDigitSquare = ta.getBoolean(R.styleable.PinEntryEditText_pinBackgroundIsSquare, mIsDigitSquare);
mPinBackground = ta.getDrawable(R.styleable.PinEntryEditText_pinBackgroundDrawable);
ColorStateList colors = ta.getColorStateList(R.styleable.PinEntryEditText_pinLineColors);
if (colors != null) {
mColorStates = colors;
}
} finally {
ta.recycle();
}
mCharPaint = new Paint(getPaint());
mLastCharPaint = new Paint(getPaint());
mSingleCharPaint = new Paint(getPaint());
mLinesPaint = new Paint(getPaint());
mLinesPaint.setStrokeWidth(mLineStroke);
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.colorControlActivated,
outValue, true);
int colorSelected = outValue.data;
mColors[0] = colorSelected;
int colorFocused = isInEditMode() ? Color.GRAY : ContextCompat.getColor(context, R.color.pin_normal);
mColors[1] = colorFocused;
int colorUnfocused = isInEditMode() ? Color.GRAY : ContextCompat.getColor(context, R.color.pin_normal);
mColors[2] = colorUnfocused;
setBackgroundResource(0);
mMaxLength = attrs.getAttributeIntValue(XML_NAMESPACE_ANDROID, "maxLength", 4);
mNumChars = mMaxLength;
//Disable copy paste
super.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
public void onDestroyActionMode(ActionMode mode) {
}
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return false;
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
});
// When tapped, move cursor to end of text.
super.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
setSelection(getText().length());
if (mClickListener != null) {
mClickListener.onClick(v);
}
}
});
super.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
setSelection(getText().length());
return true;
}
});
//If input type is password and no mask is set, use a default mask
if ((getInputType() & InputType.TYPE_TEXT_VARIATION_PASSWORD) == InputType.TYPE_TEXT_VARIATION_PASSWORD && TextUtils.isEmpty(mMask)) {
mMask = "\u25CF";
} else if ((getInputType() & InputType.TYPE_NUMBER_VARIATION_PASSWORD) == InputType.TYPE_NUMBER_VARIATION_PASSWORD && TextUtils.isEmpty(mMask)) {
mMask = "\u25CF";
}
if (!TextUtils.isEmpty(mMask)) {
mMaskChars = getMaskChars();
}
//Height of the characters, used if there is a background drawable
getPaint().getTextBounds("|", 0, 1, mTextHeight);
mAnimate = mAnimatedType > -1;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mOriginalTextColors = getTextColors();
if (mOriginalTextColors != null) {
mLastCharPaint.setColor(mOriginalTextColors.getDefaultColor());
mCharPaint.setColor(mOriginalTextColors.getDefaultColor());
mSingleCharPaint.setColor(getCurrentHintTextColor());
}
int availableWidth = getWidth() - ViewCompat.getPaddingEnd(this) - ViewCompat.getPaddingStart(this);
if (mSpace < 0) {
mCharSize = (availableWidth / (mNumChars * 2 - 1));
} else {
mCharSize = (availableWidth - (mSpace * (mNumChars - 1))) / mNumChars;
}
mLineCoords = new RectF[(int) mNumChars];
mCharBottom = new float[(int) mNumChars];
int startX;
int bottom = getHeight() - getPaddingBottom();
int rtlFlag;
final boolean isLayoutRtl = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL;
if (isLayoutRtl) {
rtlFlag = -1;
startX = (int) (getWidth() - ViewCompat.getPaddingStart(this) - mCharSize);
} else {
rtlFlag = 1;
startX = ViewCompat.getPaddingStart(this);
}
for (int i = 0; i < mNumChars; i++) {
mLineCoords[i] = new RectF(startX, bottom, startX + mCharSize, bottom);
if (mPinBackground != null) {
if (mIsDigitSquare) {
mLineCoords[i].top = getPaddingTop();
mLineCoords[i].right = startX + mLineCoords[i].height();
} else {
mLineCoords[i].top -= mTextHeight.height() + mTextBottomPadding * 2;
}
}
if (mSpace < 0) {
startX += rtlFlag * mCharSize * 2;
} else {
startX += rtlFlag * (mCharSize + mSpace);
}
mCharBottom[i] = mLineCoords[i].bottom - mTextBottomPadding;
}
}
#Override
public void setOnClickListener(View.OnClickListener l) {
mClickListener = l;
}
#Override
public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) {
throw new RuntimeException("setCustomSelectionActionModeCallback() not supported.");
}
#Override
protected void onDraw(Canvas canvas) {
//super.onDraw(canvas);
CharSequence text = getFullText();
int textLength = text.length();
float[] textWidths = new float[textLength];
getPaint().getTextWidths(text, 0, textLength, textWidths);
float hintWidth = 0;
if (mSingleCharHint != null) {
float[] hintWidths = new float[mSingleCharHint.length()];
getPaint().getTextWidths(mSingleCharHint, hintWidths);
for (float i : hintWidths) {
hintWidth += i;
}
}
for (int i = 0; i < mNumChars; i++) {
//If a background for the pin characters is specified, it should be behind the characters.
if (mPinBackground != null) {
updateDrawableState(i < textLength, i == textLength);
mPinBackground.setBounds((int) mLineCoords[i].left, (int) mLineCoords[i].top, (int) mLineCoords[i].right, (int) mLineCoords[i].bottom);
mPinBackground.draw(canvas);
}
float middle = mLineCoords[i].left + mCharSize / 2;
if (textLength > i) {
if (!mAnimate || i != textLength - 1) {
canvas.drawText(text, i, i + 1, middle - textWidths[i] / 2, mCharBottom[i], mCharPaint);
} else {
canvas.drawText(text, i, i + 1, middle - textWidths[i] / 2, mCharBottom[i], mLastCharPaint);
}
} else if (mSingleCharHint != null) {
canvas.drawText(mSingleCharHint, middle - hintWidth / 2, mCharBottom[i], mSingleCharPaint);
}
//The lines should be in front of the text (because that's how I want it).
if (mPinBackground == null) {
updateColorForLines(i <= textLength);
canvas.drawLine(mLineCoords[i].left, mLineCoords[i].top, mLineCoords[i].right, mLineCoords[i].bottom, mLinesPaint);
}
}
}
private CharSequence getFullText() {
if (mMask == null) {
return getText();
} else {
return getMaskChars();
}
}
private StringBuilder getMaskChars() {
if (mMaskChars == null) {
mMaskChars = new StringBuilder();
}
int textLength = getText().length();
while (mMaskChars.length() != textLength) {
if (mMaskChars.length() < textLength) {
mMaskChars.append(mMask);
} else {
mMaskChars.deleteCharAt(mMaskChars.length() - 1);
}
}
return mMaskChars;
}
private int getColorForState(int... states) {
return mColorStates.getColorForState(states, Color.GRAY);
}
/**
* #param hasTextOrIsNext Is the color for a character that has been typed or is
* the next character to be typed?
*/
protected void updateColorForLines(boolean hasTextOrIsNext) {
if (mHasError) {
mLinesPaint.setColor(getColorForState(android.R.attr.state_active));
} else if (isFocused()) {
mLinesPaint.setStrokeWidth(mLineStrokeSelected);
mLinesPaint.setColor(getColorForState(android.R.attr.state_focused));
if (hasTextOrIsNext) {
mLinesPaint.setColor(getColorForState(android.R.attr.state_selected));
}
} else {
mLinesPaint.setStrokeWidth(mLineStroke);
mLinesPaint.setColor(getColorForState(-android.R.attr.state_focused));
}
}
protected void updateDrawableState(boolean hasText, boolean isNext) {
if (mHasError) {
mPinBackground.setState(new int[]{android.R.attr.state_active});
} else if (isFocused()) {
mPinBackground.setState(new int[]{android.R.attr.state_focused});
if (isNext) {
mPinBackground.setState(new int[]{android.R.attr.state_focused, android.R.attr.state_selected});
} else if (hasText) {
mPinBackground.setState(new int[]{android.R.attr.state_focused, android.R.attr.state_checked});
}
} else {
mPinBackground.setState(new int[]{-android.R.attr.state_focused});
}
}
public void setError(boolean hasError) {
mHasError = hasError;
}
public boolean isError() {
return mHasError;
}
/**
* Request focus on this PinEntryEditText
*/
public void focus() {
requestFocus();
// Show keyboard
InputMethodManager inputMethodManager = (InputMethodManager) getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(this, 0);
}
#Override
protected void onTextChanged(CharSequence text, final int start, int lengthBefore, final int lengthAfter) {
setError(false);
if (mLineCoords == null || !mAnimate) {
if (mOnPinEnteredListener != null && text.length() == mMaxLength) {
mOnPinEnteredListener.onPinEntered(text);
}
return;
}
if (mAnimatedType == -1) {
invalidate();
return;
}
if (lengthAfter > lengthBefore) {
if (mAnimatedType == 0) {
animatePopIn();
} else {
animateBottomUp(text, start);
}
}
}
private void animatePopIn() {
ValueAnimator va = ValueAnimator.ofFloat(1, getPaint().getTextSize());
va.setDuration(200);
va.setInterpolator(new OvershootInterpolator());
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mLastCharPaint.setTextSize((Float) animation.getAnimatedValue());
PinEntryEditText.this.invalidate();
}
});
if (getText().length() == mMaxLength && mOnPinEnteredListener != null) {
va.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
mOnPinEnteredListener.onPinEntered(getText());
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
}
va.start();
}
private void animateBottomUp(CharSequence text, final int start) {
mCharBottom[start] = mLineCoords[start].bottom - mTextBottomPadding;
ValueAnimator animUp = ValueAnimator.ofFloat(mCharBottom[start] + getPaint().getTextSize(), mCharBottom[start]);
animUp.setDuration(300);
animUp.setInterpolator(new OvershootInterpolator());
animUp.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
Float value = (Float) animation.getAnimatedValue();
mCharBottom[start] = value;
PinEntryEditText.this.invalidate();
}
});
mLastCharPaint.setAlpha(255);
ValueAnimator animAlpha = ValueAnimator.ofInt(0, 255);
animAlpha.setDuration(300);
animAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
mLastCharPaint.setAlpha(value);
}
});
AnimatorSet set = new AnimatorSet();
if (text.length() == mMaxLength && mOnPinEnteredListener != null) {
set.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
mOnPinEnteredListener.onPinEntered(getText());
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
}
set.playTogether(animUp, animAlpha);
set.start();
}
public void setAnimateText(boolean animate) {
mAnimate = animate;
}
public void setOnPinEnteredListener(OnPinEnteredListener l) {
mOnPinEnteredListener = l;
}
public interface OnPinEnteredListener {
void onPinEntered(CharSequence str);
}
}
Following is the xml
<com.cartoon.customLayout.PinEntryEditText
android:id="#+id/et_activity_mobile_pinEntryEditText"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:background="#null"
android:cursorVisible="true"
android:digits="1234567890"
android:focusable="true"
android:imeOptions="actionDone"
android:inputType="number|phone"
android:maxLength="10"
android:textColor="#android:color/white"
android:textIsSelectable="true"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.40"
app:pinAnimationType="fromBottom"
app:pinBackgroundDrawable="#drawable/bg_pin"
app:pinCharacterSpacing="4dp" />
Suppose the user enters his mobile number and the second digit entered is wrong then the problem is, he/she has to erase the entered characters leading to the second character for a change.
I went through the following posts but they are of no help
Custom EditText is not showing keyboard on focus
Custom Android pin code entry widget
https://github.com/ChaosLeong/PinView
Setting focusable, clickable or focusableInTouchMode does not work. Tried both in XML and code.
The below library does solve my problem but it has some other problem for which I have raised an issue
https://github.com/mukeshsolanki/android-otpview-pinview
Issue: https://github.com/mukeshsolanki/android-otpview-pinview/issues/26
Of course I could take 10 edittext and achieve the result which I want but that is not the right way and I will resort to that option in case I am not able to find the right solution to this one.
The answer is in your java code. As mentioned in the comment just remove this from the init() method.
// When tapped, move cursor to end of text.
super.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
setSelection(getText().length());
if (mClickListener != null) {
mClickListener.onClick(v);
}
}
});
Please make the following changes:
In your PinEntryEditText class:
// When tapped, move cursor to end of text.
super.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//setSelection(getText().length());
if (mClickListener != null) {
mClickListener.onClick(v);
}
}
});
I think setSelection(getText().length()); was setting selection to the end of the EditText no matter where the click even occured.
And in your XML, do these: android:cursorVisible="true" and android:textIsSelectable="true"
I have a trapezoid shape created in custom view using below code.
#Override
protected void onDraw(Canvas canvas) {
trapezoidPath.moveTo(0,0);
trapezoidPath.lineTo(getWidth() ,0);
trapezoidPath.lineTo(getWidth() , altitude);
trapezoidPath.lineTo(0,getHeight());
trapezoidPath.lineTo(0,0);
trapezoidPath.close();
canvas.drawPath(trapezoidPath,paintTrapezoid);
}
The drawn shape looks like this.
I want to move (0,height) point to top until trapezoid shape become a rectangle. After that I want to move bottom line up until shape become a line.
Is there any way to access created path lines and it's point and manipulate them to achieve what I want ? If not how can I achieve this ?
I have to animate this shape base on user response. Thank you.
Use an ObjectAnimator to change the variables you use in onDraw. Below is an example of how you could implement this.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TrapezoidView trapezoidView = findViewById(R.id.trapezoid);
final Button resetButton = findViewById(R.id.btn_reset);
resetButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
trapezoidView.reset();
}
});
final Button animateButton = findViewById(R.id.btn_animate);
animateButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
animateButton.setEnabled(false);
resetButton.setEnabled(false);
trapezoidView.toNewState();
}
});
trapezoidView.setListener(new TrapezoidView.TrapezoidListener() {
#Override
public void onNewState() {
animateButton.setEnabled(true);
resetButton.setEnabled(true);
}
});
}
}
TrapezoidView.java
public class TrapezoidView extends View {
public interface TrapezoidListener {
void onNewState();
}
public static final int TRAPEZOID_STATE = 0;
public static final int RECTANGLE_STATE = 1;
public static final int LINE_STATE = 2;
private int mState = TRAPEZOID_STATE;
private Paint mTrapezoidPaint;
private Path mTrapezoidPath = new Path();
private int mAnimationDuration = 5000; // 5 s in millis
private float mRectangleHeight = dpTopx(200);
private float mAltitude = mRectangleHeight;
private TrapezoidListener mListener;
public TrapezoidView(Context context, AttributeSet attrs) {
super(context, attrs);
mTrapezoidPaint = new Paint();
mTrapezoidPaint.setColor(Color.BLACK);
mTrapezoidPaint.setStrokeWidth(5.0f);
mTrapezoidPaint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mTrapezoidPath.reset();
if (mState == TRAPEZOID_STATE) {
mAltitude = getHeight();
}
mTrapezoidPath.moveTo(0, 0);
mTrapezoidPath.lineTo(getWidth(), 0);
if (mState == LINE_STATE) {
mTrapezoidPath.lineTo(getWidth(), mAltitude);
mTrapezoidPath.lineTo(0, mAltitude);
} else {
mTrapezoidPath.lineTo(getWidth(), mRectangleHeight);
mTrapezoidPath.lineTo(0, mAltitude);
}
mTrapezoidPath.lineTo(0, 0);
mTrapezoidPath.close();
canvas.drawPath(mTrapezoidPath, mTrapezoidPaint);
}
private float dpTopx(int dp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
public float getAltitude() {
return mAltitude;
}
public void setAltitude(float altitude) {
this.mAltitude = altitude;
invalidate();
}
public void reset() {
mState = TRAPEZOID_STATE;
mRectangleHeight = dpTopx(200);
mAltitude = mRectangleHeight;
invalidate();
}
public void setListener(TrapezoidListener listener) {
mListener = listener;
}
public void toNewState() {
if (mState == LINE_STATE) {
mListener.onNewState();
return;
}
float start;
float target;
final int targetState = mState == TRAPEZOID_STATE ? RECTANGLE_STATE : LINE_STATE;
if (targetState == RECTANGLE_STATE) {
start = getHeight();
target = mRectangleHeight;
} else {
start = mAltitude;
target = 0.0f;
}
ObjectAnimator stateAnimation = ObjectAnimator.ofFloat(TrapezoidView.this, "Altitude", start);
stateAnimation.setFloatValues(target);
stateAnimation.setDuration(mAnimationDuration);
stateAnimation.addListener(
new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
mState = targetState;
}
#Override
public void onAnimationEnd(Animator animation) {
mListener.onNewState();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
}
);
stateAnimation.start();
}
}
activity_main.xml
<LinearLayout 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:orientation="vertical"
android:padding="50dp"
tools:context="test.example.MainActivity">
<test.example.TrapezoidView
android:id="#+id/trapezoid"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.9" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.1"
android:orientation="horizontal">
<Button
android:id="#+id/btn_animate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Animate!" />
<Button
android:id="#+id/btn_reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Reset" />
</LinearLayout>
</LinearLayout>
I have a rounded rectangle button in my activity. Once I click the button, it should slightly compress/shrink (height of the button should not change) and it should change to a circle shape. How can I achieve this using the anim folder in XML?
Currently my code is:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
android:duration="400"
android:fromXScale="1"
android:fromYScale="1"
android:pivotX="50%"
android:toXScale="0.1"
android:toYScale="1" />
<set>
<alpha
android:duration="400"
android:fromAlpha="1"
android:toAlpha="0" />
</set>
</set>
Create MorphingButton class paste below code into;
public class MorphingButton extends Button {
private Padding mPadding;
private int mHeight;
private int mWidth;
private int mColor;
private int mCornerRadius;
private int mStrokeWidth;
private int mStrokeColor;
protected boolean mAnimationInProgress;
private StrokeGradientDrawable mDrawableNormal;
private StrokeGradientDrawable mDrawablePressed;
public MorphingButton(Context context) {
super(context);
initView();
}
public MorphingButton(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public MorphingButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (mHeight == 0 && mWidth == 0 && w != 0 && h != 0) {
mHeight = getHeight();
mWidth = getWidth();
}
}
public StrokeGradientDrawable getDrawableNormal() {
return mDrawableNormal;
}
public void morph(#NonNull Params params) {
if (!mAnimationInProgress) {
mDrawablePressed.setColor(params.colorPressed);
mDrawablePressed.setCornerRadius(params.cornerRadius);
mDrawablePressed.setStrokeColor(params.strokeColor);
mDrawablePressed.setStrokeWidth(params.strokeWidth);
if (params.duration == 0) {
morphWithoutAnimation(params);
} else {
morphWithAnimation(params);
}
mColor = params.color;
mCornerRadius = params.cornerRadius;
mStrokeWidth = params.strokeWidth;
mStrokeColor = params.strokeColor;
}
}
private void morphWithAnimation(#NonNull final Params params) {
mAnimationInProgress = true;
setText(null);
setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
setPadding(mPadding.left, mPadding.top, mPadding.right, mPadding.bottom);
MorphingAnimation.Params animationParams = MorphingAnimation.Params.create(this)
.color(mColor, params.color)
.cornerRadius(mCornerRadius, params.cornerRadius)
.strokeWidth(mStrokeWidth, params.strokeWidth)
.strokeColor(mStrokeColor, params.strokeColor)
.height(getHeight(), params.height)
.width(getWidth(), params.width)
.duration(params.duration)
.listener(new MorphingAnimation.Listener() {
#Override
public void onAnimationEnd() {
finalizeMorphing(params);
}
});
MorphingAnimation animation = new MorphingAnimation(animationParams);
animation.start();
}
private void morphWithoutAnimation(#NonNull Params params) {
mDrawableNormal.setColor(params.color);
mDrawableNormal.setCornerRadius(params.cornerRadius);
mDrawableNormal.setStrokeColor(params.strokeColor);
mDrawableNormal.setStrokeWidth(params.strokeWidth);
if(params.width != 0 && params.height !=0) {
ViewGroup.LayoutParams layoutParams = getLayoutParams();
layoutParams.width = params.width;
layoutParams.height = params.height;
setLayoutParams(layoutParams);
}
finalizeMorphing(params);
}
private void finalizeMorphing(#NonNull Params params) {
mAnimationInProgress = false;
if (params.icon != 0 && params.text != null) {
setIconLeft(params.icon);
setText(params.text);
} else if (params.icon != 0) {
setIcon(params.icon);
} else if(params.text != null) {
setText(params.text);
}
if (params.animationListener != null) {
params.animationListener.onAnimationEnd();
}
}
public void blockTouch() {
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
}
public void unblockTouch() {
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
}
private void initView() {
mPadding = new Padding();
mPadding.left = getPaddingLeft();
mPadding.right = getPaddingRight();
mPadding.top = getPaddingTop();
mPadding.bottom = getPaddingBottom();
Resources resources = getResources();
int cornerRadius = (int) resources.getDimension(R.dimen._10sdp);
int primaryColor = resources.getColor(R.color.colorAccent);
int secondaryColor = resources.getColor(R.color.colorPrimary);
StateListDrawable background = new StateListDrawable();
mDrawableNormal = createDrawable(primaryColor, cornerRadius, 0);
mDrawablePressed = createDrawable(secondaryColor, cornerRadius, 0);
mColor = primaryColor;
mStrokeColor = primaryColor;
mCornerRadius = cornerRadius;
background.addState(new int[]{android.R.attr.state_pressed}, mDrawablePressed.getGradientDrawable());
background.addState(StateSet.WILD_CARD, mDrawableNormal.getGradientDrawable());
setBackgroundCompat(background);
}
private StrokeGradientDrawable createDrawable(int color, int cornerRadius, int strokeWidth) {
StrokeGradientDrawable drawable = new StrokeGradientDrawable(new GradientDrawable());
drawable.getGradientDrawable().setShape(GradientDrawable.RECTANGLE);
drawable.setColor(color);
drawable.setCornerRadius(cornerRadius);
drawable.setStrokeColor(color);
drawable.setStrokeWidth(strokeWidth);
return drawable;
}
#SuppressWarnings("deprecation")
private void setBackgroundCompat(#Nullable Drawable drawable) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {
setBackgroundDrawable(drawable);
} else {
setBackground(drawable);
}
}
public void setIcon(#DrawableRes final int icon) {
// post is necessary, to make sure getWidth() doesn't return 0
post(new Runnable() {
#Override
public void run() {
Drawable drawable = getResources().getDrawable(icon);
int padding = (getWidth() / 2) - (drawable.getIntrinsicWidth() / 2);
setCompoundDrawablesWithIntrinsicBounds(icon, 0, 0, 0);
setPadding(padding, 0, 0, 0);
}
});
}
public void setIconLeft(#DrawableRes int icon) {
setCompoundDrawablesWithIntrinsicBounds(icon, 0, 0, 0);
}
private class Padding {
public int left;
public int right;
public int top;
public int bottom;
}
public static class Params {
private int cornerRadius;
private int width;
private int height;
private int color;
private int colorPressed;
private int duration;
private int icon;
private int strokeWidth;
private int strokeColor;
private String text;
private MorphingAnimation.Listener animationListener;
private Params() {
}
public static Params create() {
return new Params();
}
public Params text(#NonNull String text) {
this.text = text;
return this;
}
public Params icon(#DrawableRes int icon) {
this.icon = icon;
return this;
}
public Params cornerRadius(int cornerRadius) {
this.cornerRadius = cornerRadius;
return this;
}
public Params width(int width) {
this.width = width;
return this;
}
public Params height(int height) {
this.height = height;
return this;
}
public Params color(int color) {
this.color = color;
return this;
}
public Params colorPressed(int colorPressed) {
this.colorPressed = colorPressed;
return this;
}
public Params duration(int duration) {
this.duration = duration;
return this;
}
public Params strokeWidth(int strokeWidth) {
this.strokeWidth = strokeWidth;
return this;
}
public Params strokeColor(int strokeColor) {
this.strokeColor = strokeColor;
return this;
}
public Params animationListener(MorphingAnimation.Listener animationListener) {
this.animationListener = animationListener;
return this;
}
}
public static class MorphingAnimation {
public interface Listener {
void onAnimationEnd();
}
public static class Params {
private float fromCornerRadius;
private float toCornerRadius;
private int fromHeight;
private int toHeight;
private int fromWidth;
private int toWidth;
private int fromColor;
private int toColor;
private int duration;
private int fromStrokeWidth;
private int toStrokeWidth;
private int fromStrokeColor;
private int toStrokeColor;
private MorphingButton button;
private MorphingAnimation.Listener animationListener;
private Params(#NonNull MorphingButton button) {
this.button = button;
}
public static Params create(#NonNull MorphingButton button) {
return new Params(button);
}
public Params duration(int duration) {
this.duration = duration;
return this;
}
public Params listener(#NonNull MorphingAnimation.Listener animationListener) {
this.animationListener = animationListener;
return this;
}
public Params color(int fromColor, int toColor) {
this.fromColor = fromColor;
this.toColor = toColor;
return this;
}
public Params cornerRadius(int fromCornerRadius, int toCornerRadius) {
this.fromCornerRadius = fromCornerRadius;
this.toCornerRadius = toCornerRadius;
return this;
}
public Params height(int fromHeight, int toHeight) {
this.fromHeight = fromHeight;
this.toHeight = toHeight;
return this;
}
public Params width(int fromWidth, int toWidth) {
this.fromWidth = fromWidth;
this.toWidth = toWidth;
return this;
}
public Params strokeWidth(int fromStrokeWidth, int toStrokeWidth) {
this.fromStrokeWidth = fromStrokeWidth;
this.toStrokeWidth = toStrokeWidth;
return this;
}
public Params strokeColor(int fromStrokeColor, int toStrokeColor) {
this.fromStrokeColor = fromStrokeColor;
this.toStrokeColor = toStrokeColor;
return this;
}
}
private Params mParams;
public MorphingAnimation(#NonNull Params params) {
mParams = params;
}
public void start() {
StrokeGradientDrawable background = mParams.button.getDrawableNormal();
ObjectAnimator cornerAnimation =
ObjectAnimator.ofFloat(background, "cornerRadius", mParams.fromCornerRadius, mParams.toCornerRadius);
ObjectAnimator strokeWidthAnimation =
ObjectAnimator.ofInt(background, "strokeWidth", mParams.fromStrokeWidth, mParams.toStrokeWidth);
ObjectAnimator strokeColorAnimation = ObjectAnimator.ofInt(background, "strokeColor", mParams.fromStrokeColor, mParams.toStrokeColor);
strokeColorAnimation.setEvaluator(new ArgbEvaluator());
ObjectAnimator bgColorAnimation = ObjectAnimator.ofInt(background, "color", mParams.fromColor, mParams.toColor);
bgColorAnimation.setEvaluator(new ArgbEvaluator());
ValueAnimator heightAnimation = ValueAnimator.ofInt(mParams.fromHeight, mParams.toHeight);
heightAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = mParams.button.getLayoutParams();
layoutParams.height = val;
mParams.button.setLayoutParams(layoutParams);
}
});
ValueAnimator widthAnimation = ValueAnimator.ofInt(mParams.fromWidth, mParams.toWidth);
widthAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = mParams.button.getLayoutParams();
layoutParams.width = val;
mParams.button.setLayoutParams(layoutParams);
}
});
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(mParams.duration);
animatorSet.playTogether(strokeWidthAnimation, strokeColorAnimation, cornerAnimation, bgColorAnimation,
heightAnimation, widthAnimation);
animatorSet.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
if (mParams.animationListener != null) {
mParams.animationListener.onAnimationEnd();
}
}
});
animatorSet.start();
}
}
public class StrokeGradientDrawable {
private int mStrokeWidth;
private int mStrokeColor;
private GradientDrawable mGradientDrawable;
private float mRadius;
private int mColor;
public StrokeGradientDrawable(GradientDrawable drawable) {
mGradientDrawable = drawable;
}
public int getStrokeWidth() {
return mStrokeWidth;
}
public void setStrokeWidth(int strokeWidth) {
mStrokeWidth = strokeWidth;
mGradientDrawable.setStroke(strokeWidth, getStrokeColor());
}
public int getStrokeColor() {
return mStrokeColor;
}
public void setStrokeColor(int strokeColor) {
mStrokeColor = strokeColor;
mGradientDrawable.setStroke(getStrokeWidth(), strokeColor);
}
public void setCornerRadius(float radius) {
mRadius = radius;
mGradientDrawable.setCornerRadius(radius);
}
public void setColor(int color) {
mColor = color;
mGradientDrawable.setColor(color);
}
public int getColor() {
return mColor;
}
public float getRadius() {
return mRadius;
}
public GradientDrawable getGradientDrawable() {
return mGradientDrawable;
}
}
}
Add this widget in your layout.xml
<com.test.widgets.MorphingButton
android:id="#+id/btnDone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="DONE"/>
In Activity to animate view onButtonClick
write below code inside `MorphingButton` onClick
MorphingButton.Params circle = MorphingButton.Params.create()//To animate in circle from rect-round
.duration(500)
.cornerRadius((int) getResources().getDimension(R.dimen._100sdp))
.width((int) getResources().getDimension(R.dimen._56sdp))
.height((int) getResources().getDimension(R.dimen._56sdp))
.color(Color.BLUE) // normal state color
.colorPressed(Color.GREEN) // pressed state color
.icon(R.drawable.ic_loader); // icon
btnDone.morph(circle);
//To animate from circle to rect-round to this demo purpose
btnDone.postDelayed(new Runnable() {
#Override
public void run() {
MorphingButton.Params square = MorphingButton.Params.create()
.duration(500)
.cornerRadius((int) getResources().getDimension(R.dimen._100sdp))
.width((int) getResources().getDimension(R.dimen._100sdp))
.height((int) getResources().getDimension(R.dimen._56sdp))
.color(Color.BLUE) // normal state color
.colorPressed(Color.GREEN) // pressed state color
.icon(R.drawable.ic_loader) // icon
.text("DONE");
btnDone.morph(square);
}
}, 5000);
In my app I have a surface view with particle explosion animation.I was trying to make that surface view, transparent. After making it transparent, explode animation have a problem. I have attached screenshots of original animation and after making surface view transparent. When setting background color black, its working fine.
MainActivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout surface = (LinearLayout)findViewById(R.id.middleSurface);
surface.addView(new MainGamePanel(this));
}
}
MainGamePanel.java
public class MainGamePanel extends SurfaceView implements
SurfaceHolder.Callback {
private static final String TAG = MainGamePanel.class.getSimpleName();
private static final int EXPLOSION_SIZE = 200;
private MainThread thread;
private Explosion explosion;
// the fps to be displayed
private String avgFps;
public void setAvgFps(String avgFps) {
this.avgFps = avgFps;
}
public MainGamePanel(Context context) {
super(context);
// adding the callback (this) to the surface holder to intercept events
this.setBackgroundColor(Color.TRANSPARENT); //To make canvas transparent
this.setZOrderOnTop(true); //To make canvas transparent
getHolder().setFormat(PixelFormat.TRANSPARENT); //To make canvas transparent!
getHolder().addCallback(this);
// make the GamePanel focusable so it can handle events
setFocusable(true);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// create the game loop thread
thread = new MainThread(getHolder(), this);
// at this point the surface is created and
// we can safely start the game loop
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Surface is being destroyed");
// tell the thread to shut down and wait for it to finish
// this is a clean shutdown
boolean retry = true;
while (retry) {
try {
thread.setRunning(false);
thread.join();
retry = false;
} catch (InterruptedException e) {
// try again shutting down the thread
}
}
Log.d(TAG, "Thread was shut down cleanly");
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// handle touch
// check if explosion is null or if it is still active
if (explosion == null || explosion.getState() == Explosion.STATE_DEAD) {
explosion = new Explosion(EXPLOSION_SIZE, (int)event.getX(), (int)event.getY());
}
}
return true;
}
public void render(Canvas canvas) {
canvas.drawColor(Color.argb(0, 0, 0, 0)); //To make canvas transparent
// render explosions
if (explosion != null) {
explosion.draw(canvas);
}
// display fps
//displayFps(canvas, avgFps);
// display border
/*Paint paint = new Paint();
paint.setColor(Color.GREEN);
canvas.drawLines(new float[]{
0,0, canvas.getWidth()-1,0,
canvas.getWidth()-1,0, canvas.getWidth()-1,canvas.getHeight()-1,
canvas.getWidth()-1,canvas.getHeight()-1, 0,canvas.getHeight()-1,
0,canvas.getHeight()-1, 0,0
}, paint);*/
}
/**
* This is the game update method. It iterates through all the objects
* and calls their update method if they have one or calls specific
* engine's update method.
*/
public void update() {
// update explosions
if (explosion != null && explosion.isAlive()) {
explosion.update(getHolder().getSurfaceFrame());
}
}
private void displayFps(Canvas canvas, String fps) {
if (canvas != null && fps != null) {
Paint paint = new Paint();
paint.setARGB(255, 255, 255, 255);
canvas.drawText(fps, this.getWidth() - 50, 20, paint);
}
}
}
Explosion.java
public class Explosion {
private static final String TAG = Explosion.class.getSimpleName();
public static final int STATE_ALIVE = 0; // at least 1 particle is alive
public static final int STATE_DEAD = 1; // all particles are dead
private Particle[] particles; // particles in the explosion
private int x, y; // the explosion's origin
private float gravity; // the gravity of the explosion (+ upward, - down)
private float wind; // speed of wind on horizontal
private int size; // number of particles
private int state; // whether it's still active or not
public Explosion(int particleNr, int x, int y) {
Log.d(TAG, "Explosion created at " + x + "," + y);
this.state = STATE_ALIVE;
this.particles = new Particle[particleNr];
for (int i = 0; i < this.particles.length; i++) {
Particle p = new Particle(x, y);
this.particles[i] = p;
}
this.size = particleNr;
}
public Particle[] getParticles() {
return particles;
}
public void setParticles(Particle[] particles) {
this.particles = particles;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public float getGravity() {
return gravity;
}
public void setGravity(float gravity) {
this.gravity = gravity;
}
public float getWind() {
return wind;
}
public void setWind(float wind) {
this.wind = wind;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
// helper methods -------------------------
public boolean isAlive() {
return this.state == STATE_ALIVE;
}
public boolean isDead() {
return this.state == STATE_DEAD;
}
public void update() {
if (this.state != STATE_DEAD) {
boolean isDead = true;
for (int i = 0; i < this.particles.length; i++) {
if (this.particles[i].isAlive()) {
this.particles[i].update();
isDead = false;
}
}
if (isDead)
this.state = STATE_DEAD;
}
}
public void update(Rect container) {
if (this.state != STATE_DEAD) {
boolean isDead = true;
for (int i = 0; i < this.particles.length; i++) {
if (this.particles[i].isAlive()) {
this.particles[i].update(container);
// this.particles[i].update();
isDead = false;
}
}
if (isDead)
this.state = STATE_DEAD;
}
}
public void draw(Canvas canvas) {
for(int i = 0; i < this.particles.length; i++) {
if (this.particles[i].isAlive()) {
this.particles[i].draw(canvas);
}
}
}
}
Particle.java
public class Particle {
public static final int STATE_ALIVE = 0; // particle is alive
public static final int STATE_DEAD = 1; // particle is dead
public static final int DEFAULT_LIFETIME = 200; // play with this
public static final int MAX_DIMENSION = 5; // the maximum width or height
public static final int MAX_SPEED = 10; // maximum speed (per update)
private int state; // particle is alive or dead
private float widht; // width of the particle
private float height; // height of the particle
private float x, y; // horizontal and vertical position
private double xv, yv; // vertical and horizontal velocity
private int age; // current age of the particle
private int lifetime; // particle dies when it reaches this value
private int color; // the color of the particle
private Paint paint; // internal use to avoid instantiation
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public float getWidht() {
return widht;
}
public void setWidht(float widht) {
this.widht = widht;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public double getXv() {
return xv;
}
public void setXv(double xv) {
this.xv = xv;
}
public double getYv() {
return yv;
}
public void setYv(double yv) {
this.yv = yv;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getLifetime() {
return lifetime;
}
public void setLifetime(int lifetime) {
this.lifetime = lifetime;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
// helper methods -------------------------
public boolean isAlive() {
return this.state == STATE_ALIVE;
}
public boolean isDead() {
return this.state == STATE_DEAD;
}
public Particle(int x, int y) {
this.x = x;
this.y = y;
this.state = Particle.STATE_ALIVE;
this.widht = rndInt(1, MAX_DIMENSION);
this.height = this.widht;
// this.height = rnd(1, MAX_DIMENSION);
this.lifetime = DEFAULT_LIFETIME;
this.age = 0;
this.xv = (rndDbl(0, MAX_SPEED * 2) - MAX_SPEED);
this.yv = (rndDbl(0, MAX_SPEED * 2) - MAX_SPEED);
// smoothing out the diagonal speed
if (xv * xv + yv * yv > MAX_SPEED * MAX_SPEED) {
xv *= 0.7;
yv *= 0.7;
}
this.color = Color.argb(255, rndInt(0, 255), rndInt(0, 255), rndInt(0, 255));
this.paint = new Paint(this.color);
}
/**
* Resets the particle
* #param x
* #param y
*/
public void reset(float x, float y) {
this.state = Particle.STATE_ALIVE;
this.x = x;
this.y = y;
this.age = 0;
}
// Return an integer that ranges from min inclusive to max inclusive.
static int rndInt(int min, int max) {
return (int) (min + Math.random() * (max - min + 1));
}
static double rndDbl(double min, double max) {
return min + (max - min) * Math.random();
}
public void update() {
if (this.state != STATE_DEAD) {
this.x += this.xv;
this.y += this.yv;
// extract alpha
int a = this.color >>> 24;
a -= 2; // fade by 5
if (a <= 0) { // if reached transparency kill the particle
this.state = STATE_DEAD;
} else {
this.color = (this.color & 0x00ffffff) + (a << 24); // set the new alpha
this.paint.setAlpha(a);
this.age++; // increase the age of the particle
// this.widht *= 1.05;
// this.height *= 1.05;
}
if (this.age >= this.lifetime) { // reached the end if its life
this.state = STATE_DEAD;
}
// http://lab.polygonal.de/2007/05/10/bitwise-gems-fast-integer-math/
//32bit
// var color:uint = 0xff336699;
// var a:uint = color >>> 24;
// var r:uint = color >>> 16 & 0xFF;
// var g:uint = color >>> 8 & 0xFF;
// var b:uint = color & 0xFF;
}
}
public void update(Rect container) {
// update with collision
if (this.isAlive()) {
if (this.x <= container.left || this.x >= container.right - this.widht) {
this.xv *= -1;
}
// Bottom is 480 and top is 0 !!!
if (this.y <= container.top || this.y >= container.bottom - this.height) {
this.yv *= -1;
}
}
update();
}
public void draw(Canvas canvas) {
// paint.setARGB(255, 128, 255, 50);
paint.setColor(this.color);
canvas.drawRect(this.x, this.y, this.x + this.widht, this.y + this.height, paint);
// canvas.drawCircle(x, y, widht, paint);
}
}
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="#556168"
tools:context="com.offero.MainActivity$PlaceholderFragment" >
<ImageView
android:id="#+id/imageView4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="#drawable/bg" />
<ImageView
android:id="#+id/imageView2"
android:layout_width="70dp"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="5dp"
android:src="#drawable/cloud2" />
<ImageView
android:id="#+id/imageView1"
android:layout_width="70dp"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginLeft="5dp"
android:src="#drawable/cloud1" />
<ImageView
android:id="#+id/ImageView02"
android:layout_width="70dp"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/imageView2"
android:layout_alignParentBottom="true"
android:layout_marginRight="5dp"
android:src="#drawable/cloud2" />
<ImageView
android:id="#+id/ImageView01"
android:layout_width="70dp"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/imageView1"
android:layout_alignParentBottom="true"
android:layout_marginLeft="5dp"
android:src="#drawable/cloud1" />
<ImageView
android:id="#+id/ImageView03"
android:layout_width="70dp"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:src="#drawable/cloud1" />
<ImageView
android:id="#+id/imageView3"
android:layout_width="wrap_content"
android:layout_height="150dp"
android:layout_centerInParent="true"
android:src="#drawable/rocket" />
<ImageView
android:id="#+id/imageView5"
android:layout_width="wrap_content"
android:layout_height="58dp"
android:layout_alignTop="#+id/imageView3"
android:layout_centerHorizontal="true"
android:src="#drawable/rocketpeice2" />
<ImageView
android:id="#+id/imageView6"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:layout_below="#+id/imageView5"
android:layout_centerHorizontal="true"
android:src="#drawable/rocketpeice1" />
<ImageView
android:id="#+id/imageView_star1"
android:layout_width="wrap_content"
android:layout_height="10dp"
android:layout_alignTop="#+id/imageView6"
android:layout_centerHorizontal="true"
android:src="#drawable/star" />
<ImageView
android:id="#+id/imageView_star2"
android:layout_width="wrap_content"
android:layout_height="15dp"
android:layout_alignTop="#+id/imageView6"
android:layout_centerHorizontal="true"
android:src="#drawable/star" />
<ImageView
android:id="#+id/ImageView_star3"
android:layout_width="wrap_content"
android:layout_height="7dp"
android:layout_alignTop="#+id/imageView6"
android:layout_centerHorizontal="true"
android:src="#drawable/star" />
<ImageView
android:id="#+id/ImageView_star4"
android:layout_width="wrap_content"
android:layout_height="12dp"
android:layout_alignBottom="#+id/ImageView_star3"
android:layout_centerHorizontal="true"
android:src="#drawable/star" />
<ImageView
android:id="#+id/ImageView_star5"
android:layout_width="wrap_content"
android:layout_height="10dp"
android:layout_alignLeft="#+id/imageView_star1"
android:layout_below="#+id/imageView5"
android:src="#drawable/star" />
<ImageView
android:id="#+id/ImageView_star6"
android:layout_width="wrap_content"
android:layout_height="15dp"
android:layout_alignLeft="#+id/imageView_star1"
android:layout_alignTop="#+id/imageView6"
android:src="#drawable/star" />
<ImageView
android:id="#+id/ImageView_star7"
android:layout_width="wrap_content"
android:layout_height="7dp"
android:layout_alignLeft="#+id/imageView_star1"
android:layout_below="#+id/ImageView_star3"
android:src="#drawable/star" />
<ImageView
android:id="#+id/ImageView_star8"
android:layout_width="wrap_content"
android:layout_height="12dp"
android:layout_alignLeft="#+id/imageView_star1"
android:layout_alignTop="#+id/imageView6"
android:src="#drawable/star" />
<LinearLayout
android:id="#+id/middleSurface"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
</LinearLayout>
The below code draws a colour over the canvas.
canvas.drawColor(Color.argb(0, 0, 0, 0)); //To make canvas transparent
I assume you were drawing black over before. Which would draw black, then the new particle positions. Aka clearing the canvas between draws.
Now that you are drawing clear over the canvas between draws, you are literally doing nothing. As drawing nothing has no effect.
What you need to actually do is clear the canvas.
you need to use the code:
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
This draws Transparent, but also tells it not to merge with existing particles, but to actually clear them :)
Ref: https://stackoverflow.com/a/10882301/940834