When I'm trying to scale my canvas to a draw SCALED view, my view is actually scaled, but view is getting clipped. (probably because of its layout parameters?)
public void onDraw(Canvas canvas) {
canvas.scale(2f, 2f);
view.draw(canvas);
}
simple image:
image after new onDraw called, for example when I click this button:
The button should be full sized when canvas is scaled. Do you have any ideas how to solve it?
p.s. call of
view.invalidate();
view.requestLayout();
doesn't help.
I'm using MyDragShadowBuilder because I want my view to be double sized when I drag the view.
private final class MyDragShadowBuilder extends DragShadowBuilder {
public MyDragShadowBuilder(View view) {
super(view);
}
#Override
public void onDrawShadow(Canvas canvas) {
final View view = getView();
if (view != null) {
canvas.scale(2f, 2f);
view.draw(canvas);
} else {
Log.e("DragShadowBuilder", "Asked to draw drag shadow but no view");
}
}
I add my view into my Absolute Layout implementation with WRAP_CONTENT layout properties
I ran into the same trouble. After some time i found a way to make it work :)
This scales the original view by a factor of 4.
private static class MyDragShadowBuilder extends View.DragShadowBuilder {
private static final int SCALING_FACTOR = 4;
public MyDragShadowBuilder(View view) {
super(view);
}
#Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
View v = getView();
final int width = v.getWidth() * SCALING_FACTOR;
final int height = v.getHeight() * SCALING_FACTOR;
shadowSize.set(width, height);
shadowTouchPoint.set(width / 2, height / 2);
}
#Override
public void onDrawShadow(Canvas canvas) {
canvas.scale(SCALING_FACTOR, SCALING_FACTOR);
getView().draw(canvas);
}
}
Related
I want to implement a drag-and-drop functionality within a recyclerview. Everything goes perfectly until I want to customize the looks of the view being dragged (not the view from which the drag event starts, I want to modify the "shadow" and keep the original view the same).
I've tried to make a bitmap out of the view being passed, but the end result is both the original item and the Shadow are modified AND the original view loses its position on the list... WTF
Here is my code:
public class ImageDragShadowBuilder extends View.DragShadowBuilder {
private Bitmap shadow;
LinearLayout linearLayout;
private ImageDragShadowBuilder() {
super();
}
public static ImageDragShadowBuilder create(Context context, View view) {
ImageDragShadowBuilder builder = new ImageDragShadowBuilder();
builder.linearLayout = (LinearLayout) view.findViewById(R.id.metric_item);
builder.linearLayout.setBackgroundResource(R.drawable.background_item_dragging);
builder.shadow = createBitmapFromView(builder.linearLayout);
return builder;
}
public View getLayout() {
return linearLayout;
}
private static Bitmap createBitmapFromView(View v) {
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
v.layout(0, 0, v.getWidth(), v.getHeight());
v.draw(new Canvas(b));
return b;
}
#Override
public void onDrawShadow(Canvas canvas) {
canvas.drawBitmap(shadow, 0, 0, null);
}
#Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
shadowSize.x = shadow.getWidth();
shadowSize.y = shadow.getHeight();
shadowTouchPoint.x = shadowSize.x / 2;
shadowTouchPoint.y = shadowSize.y / 2;
}
}
Any ideas?
I think that there are 2 problems :
Setting the background on the view attached to the RecyclerView affects the original item and the shadow
Triggering a layout changes the item's children position
Basically you can use the original view, but you should never modify it. A slightly modified version of your shadow builder could be :
public static ImageDragShadowBuilder create(Context context, View view) {
ImageDragShadowBuilder builder = new ImageDragShadowBuilder();
builder.linearLayout = (LinearLayout) view.findViewById(R.id.metric_item);
// do not change the original view
// we will draw the background directly later
// builder.linearLayout.setBackgroundResource(R.drawable.background_item_dragging);
builder.shadow = createBitmapFromView(builder.linearLayout);
return builder;
}
private static Bitmap createBitmapFromView(View v) {
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
// do not change the original view
// v.layout(0, 0, v.getWidth(), v.getHeight());
Canvas c = new Canvas(b);
// draw the background
Drawable background = v.getContext().getDrawable(R.drawable.background_item_dragging);
background.setBounds(0, 0, b.getWidth(), b.getHeight());
background.draw(c);
v.draw(c);
return b;
}
If you observe the above two pictures, there are markers in picture1, if user tap on marker second picture will come with available options dynamically may be 2,3 etc.
For the second picture I have written custom view and able to draw circle but unable to show images inside the circle.
public class CustomView extends ViewGroup {
private Paint paint;
public CustomView(Context context) {
super(context);
// create the Paint and set its color
paint = new Paint();
paint.setColor(0xFF1f5b83);
}
#Override
protected void dispatchDraw(Canvas canvas) {
int width = this.getWidth();
int height = this.getHeight();
canvas.drawCircle(width / 2, height / 2-64, 200, paint);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
}
For testing purpose I am adding text instead of image
public void sendMessage(View view) {
circleView = new CustomView(this);
TextView textView = new TextView(this);
textView.setText("Test");
textView.setTextColor(Color.WHITE);
textView.setBackgroundColor(Color.WHITE);
addContentView(circleView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
circleView.bringToFront();
}
How to add images inside the circle in Java class I mean Activity class?
How can I create a custom DragShadowBuilder to animate the scaling of a view thats in the canvas instead of it just scaling to a set size?
For example I have an ImageView that is used for drag and drop and when I press on the ImageView the image needs to grow in the canvas thats used for drag and drop that you would see following your finger while you are dragging it to a drop zone.
I am able to get the image to scale to a set size using the following CustomDragShadowBuilder class found here but is there a way to animate the scaling?
public static class CustomDragShadowBuilder extends View.DragShadowBuilder {
private static final int SCALING_FACTOR = 4;
public CustomDragShadowBuilder(View view) {
super(view);
}
#Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
View v = getView();
final int width = v.getWidth() * SCALING_FACTOR;
final int height = v.getHeight() * SCALING_FACTOR;
shadowSize.set(width, height);
shadowTouchPoint.set(width / 2, height / 2);
}
#Override
public void onDrawShadow(Canvas canvas) {
canvas.scale(SCALING_FACTOR, SCALING_FACTOR);
getView().draw(canvas);
}
}
best solution I found is to animate the scale before start the dragging, something like that:
final View yourView;
ObjectAnimator scaleX = ObjectAnimator.ofFloat(yourView, "scaleX",4f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(yourView, "scaleY",4f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(scaleX, scaleY);
animSetXY.setDuration(duration);
final View.DragShadowBuilder shadow = new View.DragShadowBuilder(this);
animSetXY.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
v.startDrag(dragData, // the data to be dragged
new CustomDragShadowBuilder(yourView) , // the drag shadow builder
null,
0 // flags (not currently used, set to 0)
);
}
});
animSetXY.start();
I use a ViewGroup as a container to place multiple instances of a smaller-sized icon image at arbitrary locations. When the icons are placed near the bottom or right edge of the container, they are automatically scaled to fit within the bounds of the ViewGroup. Why does this happen, and how do I prevent it? I set the clipChildren attribute on the ViewGroup to FALSE to see if that worked, but it doesn't.
The ViewGroup is added dynamically. m_iconContainer is the ViewGroup which parents the icons.
m_outerContainer = (ViewGroup) v.findViewById( R.id.outer );
m_iconContainer = new ScalingFrameLayout( getActivity() );
m_outerContainer.addView( m_iconContainer );
Icons are added in a straightforward manner:
PinImage p = new PinImage( getActivity() );
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) p.getLayoutParams();
params.leftMargin = pt.x - PIN_IMAGE_XOFFSET;
params.topMargin = pt.y - PIN_IMAGE_YOFFSET;
m_iconContainer.addView( p );
public class ScalingFrameLayout extends FrameLayout {
private float scale = 1;
public ScalingFrameLayout(Context context) {
super(context);
setWillNotDraw(false);
setClipChildren(false);
}
public void setScale(float factor) {
scale = factor;
invalidate();
}
public float getScale() {
return scale;
}
#Override
public void onDraw(Canvas canvas) {
canvas.scale(scale, scale);
super.onDraw(canvas);
}
}
protected class PinImage extends ImageView {
public PinImage( Context c ) {
super( c );
setLayoutParams( new FrameLayout.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT ));
setImageResource( R.drawable.droppin );
this.setOnClickListener( new OnClickListener() {
public void onClick( View v ) {
onCommentPinTouched( v );
}
});
}
}
I have looked at the similar questions and tried their solutions, but it didn't work for me. I'm trying to read width of an imageView, but it is returning 0. Here is the code:
public class MainActivity extends Activity {
private ImageView image1;
float centerX,centerY;
private ImageView image2;
private boolean isFirstImage = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.rotate);
image1 = (ImageView) findViewById(R.id.ImageView01);
image2 = (ImageView) findViewById(R.id.ImageView02);
image2.setVisibility(View.GONE);
if (isFirstImage) {
applyRotation(0, 90);
isFirstImage = !isFirstImage;
}
}
private void applyRotation(float start, float end) {
centerX=image1.getWidth()/2.0f;
centerY=image1.getHeight()/2.0f;
final Flip3dAnimation rotation =
new Flip3dAnimation(start, end,centerX , centerY);
rotation.setDuration(500);
rotation.setFillAfter(true);
rotation.setInterpolator(new AccelerateInterpolator());
rotation.setAnimationListener(new DisplayNextView(isFirstImage, image1,
image2));
if (isFirstImage)
{
image1.startAnimation(rotation);
} else {
image2.startAnimation(rotation);
}
}
}
Can anyone help me with this? Thanks
You need to use the onSizechanged() method.
This is called during layout when the size of this view has changed
You should use: image1.getLayoutParams().width;
You are either accessing the ImageViews width and height before it has been measured, or after you set its visibility to GONE.
image2.setVisibility(View.GONE);
Then getWidth() or getHeight() will return 0.,
You can also have a look at onWindowFocusChanged(), onSizeChanged() and onMeasure():
#Override
public void onWindowFocusChanged(boolean focus) {
super.onWindowFocusChanged(focus);
// get the imageviews width and height here
}
public class GETImageView extends ImageView {
public GETImageView (Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Drawable d = getDrawable();
if (d != null) {
int width = MeasureSpec.getSize(widthMeasureSpec);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
The ImageView have not yet draw the image, so no width or height return.
You may use a subclass to get the imageview width.
Here is some link provide you another easy methods:
How to get the width and height of an android.widget.ImageView?
How to get the width and height of an Image View in android?
Android ImageView return getWidth, getHeight zero
Hope this help you.
you can use OnGlobalLayoutListener for the ImageView image1 so you can get exact width and height occupied by the view in the layout...as
image1 = (ImageView) findViewById(R.id.ImageView01);
ViewTreeObserver viewTreeObserver = image1.getViewTreeObserver();
viewTreeObserver
.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (isFirstImage) {
applyRotation(0, 90);
isFirstImage = !isFirstImage;
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
image1.getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
else
image1.getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
});
By the way, views don't get sized until the measurement steps have been taken. See this link:
getWidth() and getHeight() of View returns 0
Maybe you should call View.measure(int,int) first.
Since the width and height is not defined until this view is draw. You can call measure by hand to enforce this view to calculate it's size;
Please using a Handler and get height and width on this with delay time >= 10
This may be useful for use:
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
getHeight();
}
private void getHeight() {
// TODO Auto-generated method stub
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Height:" + mAnimationView.getMeasuredHeight(),
Toast.LENGTH_SHORT).show();
}
}, 10L);
}