I'm implementing drag'n'drop feature into my application.
I'm moving TextView object, and I'm using DragShadowBuilder to build shadow for this textview.
The question is, is there any way to change textView text while dragging item? I mean in
DragEvent.ACTION_DRAG_ENTERED or DragEvent.ACTION_DRAG_LOCATION case?
private static class MyDragShadowBuilder extends View.DragShadowBuilder {
// The drag shadow image, defined as a drawable thing
private static Drawable shadow;
// Defines the constructor for myDragShadowBuilder
public MyDragShadowBuilder(View v) {
// Stores the View parameter passed to myDragShadowBuilder.
super(v);
// Creates a draggable image that will fill the Canvas provided by the system.
shadow = new ColorDrawable(Color.LTGRAY);
}
// Defines a callback that sends the drag shadow dimensions and touch point back to the
// system.
#Override
public void onProvideShadowMetrics (Point size, Point touch)
// Defines local variables
private int width, height;
// Sets the width of the shadow to half the width of the original View
width = getView().getWidth() / 2;
// Sets the height of the shadow to half the height of the original View
height = getView().getHeight() / 2;
// The drag shadow is a ColorDrawable. This sets its dimensions to be the same as the
// Canvas that the system will provide. As a result, the drag shadow will fill the
// Canvas.
shadow.setBounds(0, 0, width, height);
// Sets the size parameter's width and height values. These get back to the system
// through the size parameter.
size.set(width, height);
// Sets the touch point's position to be in the middle of the drag shadow
touch.set(width / 2, height / 2);
}
// Defines a callback that draws the drag shadow in a Canvas that the system constructs
// from the dimensions passed in onProvideShadowMetrics().
#Override
public void onDrawShadow(Canvas canvas) {
// Draws the ColorDrawable in the Canvas passed in from the system.
shadow.draw(canvas);
}
}
Drag n drop android
Related
I use these two methods in my project, but two values sometimes equal, sometimes not.
public class HintView extends LinearLayout {
#Override
protected void dispatchDraw(Canvas canvas) {
this.getWidth();
canvas.getWidth();
}
}
If you inspect the source code for both LinearLayout and Canvas, you'd notice:
LinearLayout's getWidth() method is actually from View.java, a class LinearLayout extends:
* Return the width of your view.
*
* #return The width of your view, in pixels.
*/
#ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
return mRight - mLeft;
}
If you on the other hand check out the Canvas' method:
/**
* Returns the width of the current drawing layer
*
* #return the width of the current drawing layer
*/
public int getWidth() {
return nGetWidth(mNativeCanvasWrapper);
}
The n prefix is Google's way to saying: this goes to native (c++) code.
The view and its canvas can and will be different if the view has paddings, margins, etc. (i don't recall of off the top of my head all the related properties) but remember that a Canvas is just a "surface" with some given dimensions, contained in a View.
All things on Screen on Android are one form of View or another. A view needs to measure, and ask its children (if a ViewGroup, which is also a View), and then, after all this is done, it can confidently start drawing (onDraw(Canvas)).
Whenever extending the class, the bar will be drawn in the vertical center of its container. I need to place text above the bar and increasing the height (I am setting an arbitrary height on the view) causes a bunch of blank space beneath the bar. I've been googling for quite some time now but all I find is regarding the thumb mostly, and I need to draw the bar on the bottom of its container.
This is what I'm trying to achieve as a single view (notice how the bar's position in not centered):
The only "useful" code I could find was this:
Window window = ((Activity) getContext()).getWindow();
window.setGravity(Gravity.BOTTOM);
WindowManager.LayoutParams wmlp = window.getAttributes();
wmlp.y = getHeight() / 4;
window.setAttributes(wmlp);
But it has no effect whatsoever.
My custom class, nothing out of the ordinary:
public class RateSeekbar extends AppCompatSeekBar {
private Paint lettersPaint;
private Paint numberPaint;
// constructors and call to init()
private void init() {
lettersPaint = new Paint();
numberPaint = new Paint();
... // style the two different paints
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
... // get a couple of strings from resources and measure them
int middleHor = getWidth() / 2;
canvas.drawText(text1, getPaddingLeft(), 40, lettersPaint);
canvas.drawText(text2, getWidth() - text2Size - getPaddingRight(), 40, lettersPaint);
canvas.drawText(String.valueOf(getProgress()), middleHor - (text3Size / 2), 50, numberPaint);
}
}
Any ideas?
I have a strange problem with the ViewOutlineProvider for a clipped View on Android L.
I try to increase the size of the clip circle while moving the finger over the screen (x coordinates etc.). (Like on the Android L lock screen, left and right icons, which increases when you drag your finger in the specific direction)
The size changing works perfectly, until the clip bounds reaches the left or bottom border of the screen, after this the clip bounds are disappeared and i don't have any clip bounds. (Nothing rounded)
The Rect values, which causes the problem:
Rect(-1, 1033 - 101, 1136) (Rect(left, top - right, bottom))
I initiate and add the view, which i want to clip, in this way:
toClipView = new ViewClipTest(getContext());
toClipView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
addView(toClipView);
The clip view has a gray background and a red ImageView as child, it extends a FrameLayout
Update code for clip bounds scaling (is called from a onTouchEvent, while ACTION_MOVE):
public void scaleClipbounds(float convertScale) {//-1 to 1
float scale = ...;
currentScale = scale;
...
post(new Runnable() {
#Override
public void run() {
setOutlineProvider(new ClipOutline(createScaledRect(currentScale)));
invalidate();
}
});
}
...
case MotionEvent.ACTION_MOVE:
newX = event.getRawX();
newY = event.getRawY();
float newScale = ...;
scaleClipbounds(newScale);
...
My ViewOutlineProvider:
public class ClipOutline extends ViewOutlineProvider {
private Rect outerLineRect;
public ClipOutline(Rect outerLineRect) {
this.outerLineRect = outerLineRect;
}
#Override
public void getOutline(View view, Outline outline) {
outline.setOval(outerLineRect);
}
}
Xml structure:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.ClipViewTestContainer
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
If I initiate the clip bounds with the same rect, which causes the issue, everything works well, until I try to increase the bounds while onTouch again.
What is my mistake here? Or how can I force that the clip bounds can be set out of the screen boundaries? Is there a more fluent way to implement such kind of feature, instead of using an ViewOutlineProvider?
The problem is that your rect you're passing into setOval (-1, 1033 - 101, 1136) has size 102 x 103, which means your outline won't be a circle, and thus won't be eligible for clipping. Only circles, rects, and round rects can be clipped to, see https://developer.android.com/reference/android/graphics/Outline.html#canClip()
Additionally, you can significantly simplify your runnable / outline provider creation by, within scaleClipBounds, updating an existing custom outline provider, and calling View#invalidateOutline().
public void scaleClipbounds(float convertScale) {
...
mOutlineProvider.setRect(rect)
invalidateOutline()
}
In my implementation, I have two images, one layed over the other. As, I move a circle object over the top image, I want to make that area inside circle transperent, so that I can see the beneath image. For example I have two images - a car image and its framework image. I overlay car image over framweork image and as i drag a circle over car image, it should show the beneath framework.
I tried to search a lot but not getting any pointers. Somewhere I read that I need to use alpha masking or image masking using porterduff and xfermode. but I did not understand.
Specifically,
How can I make the above image transperent and how can I only make the area inside circle transperent?
Thank You
I've used helpful inputs from the question PorterduffXfermode: Clear a section of a bitmap. In the example below, touch area becomes 'transparent' and it's possible to observe down_image part beneath up_image (both images are just jpg drawables in resources).
Basically there's two possible implementations:
Disabling hardware acceleration of drawing which would make PorterDuff.Mode.CLEAR to make view transparent (refer to here for more details about hardware acceleration effects):
Activity:
public class MyActivity extends Activity {
/** Overlay image */
private DrawingView mDrawingView = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
// Create the view for the xfermode test
mDrawingView = new DrawingView(this);
params.addRule(RelativeLayout.CENTER_IN_PARENT);
mDrawingView.setLayoutParams(params);
final RelativeLayout relativeLayout = new RelativeLayout(this);
relativeLayout.setBackgroundResource(R.drawable.down_image);
relativeLayout.addView(mDrawingView);
// Main part of the implementation - make layer drawing software
if (android.os.Build.VERSION.SDK_INT >= 11) {
mDrawingView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
// Show the layout with the test view
setContentView(relativeLayout);
}
}
DrawingView:
/**
* View which makes touch area transparent
*/
public class DrawingView extends View {
/** Paint to clear touch area */
private Paint mClearPaint = null;
/** Main bitmap */
private Bitmap mBitmap = null;
/** X coordinate of touch circle */
private int mXTouch = -1;
/** Y coordinate of touch circle */
private int mYTouch = -1;
/** Radius of touch circle */
private int mRadius = 0;
/**
* Default constructor
*
* #param ct {#link Context}
*/
public DrawingView(final Context ct) {
super(ct);
// Generate bitmap used for background
mBitmap = BitmapFactory.decodeResource(ct.getResources(), R.drawable.up_image);
// Generate array of paints
mClearPaint = new Paint();
mClearPaint.setARGB(255, 255, 255, 0);
mClearPaint.setStrokeWidth(20);
mClearPaint.setStyle(Paint.Style.FILL);
// Set all transfer modes
mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mRadius = getResources().getDimensionPixelSize(R.dimen.radius);
}
#Override
public void onDraw(final Canvas canv) {
// Background colour for canvas
canv.drawColor(Color.argb(255, 0, 0, 0));
// Draw the bitmap leaving small outside border to see background
canv.drawBitmap(mBitmap, null, new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()), null);
// Loop, draws 4 circles per row, in 4 rows on top of bitmap
// Drawn in the order of mClearPaint (alphabetical)
if (mXTouch > 0 && mYTouch > 0) {
canv.drawCircle(mXTouch, mYTouch, mRadius, mClearPaint);
}
}
#Override
public boolean onTouchEvent(final MotionEvent event) {
boolean handled = false;
// get touch event coordinates and make transparent circle from it
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
mXTouch = (int) event.getX();
mYTouch = (int) event.getY();
// TODO: Note, in case of large scene it's better not to use invalidate without rectangle specified
invalidate();
handled = true;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
mXTouch = -1;
mYTouch = -1;
// TODO: Note, in case of large scene it's better not to use invalidate without rectangle specified
invalidate();
handled = true;
break;
default:
// do nothing
break;
}
return super.onTouchEvent(event) || handled;
}
}
Adopt this solution, but looks like it depends on Android version, because suggested solution hasn't worked on mine 4.2 device at all.
Greets ,
How can I get the Coordinates of the Bitmap not of the screen. Screen Coordinates got by event.getX(),event.getY() methods but not getting coordinates of the bitmap. Please help anyone.
You can't get the coordinates of the bitmap directly. You need to calculate to yourself.
Use the position of the ImageView, and with that you can handle it.
Of course, when you are after Activity onCreate you can acces to any inflated (active) views parameter.
Exemple.
ImageView a;
a.getPaddingBottom();
Like all coordinats (left right etc...)
After this you need the Height and Width of the ImageView.
Nah, when you know the views position, hegiht and width you can calculate.
Example:
final ImageView iv_YourImage = (ImageView) findViewById(R.id.iv_imageview);
public boolean onTouch(View v, MotionEvent event){
int topParam = iv_YourImage.getPaddingTop();
int rightParam = iv_YourImage.getPaddingRight();
int maxTopParam = topParam+iv_YourImage.getMaxHeight();
int maxRightParam = rightParam + iv_YourImage.getMaxWidth();
if(event.getX>topParam&&event.getX<maxTopParam){
//the x coordinate is in your image... do the same to Y
}
return true;
}
look at the following code if the answer is too short:
Android: Detect touch on actual image in ImageView
the code replaces the padding and max height coding of this answer and replaces it with the actual width and height of the displayed image using getWidth() and getHeight(). And uses the screen width and height to calculate its position. If you want more detailed answers for your problem you should give more information such as where did you draw the bitmap... did you define it at the activity XML or did you draw it at a random location such as at the X and Y coordinates of a touch event, the answer depends on that information.
package com.example.img;
imports....
public Image extends Activity {
ImageView imgYourImage;
// int imgWidth;
// etc...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image);
imgYourImage = (ImageView) findViewById(R.id.yourImage);
imgDimension();
}
private void imgDimension() {
imgWidth = imgYourImage.getWidth();
// at newer api's you can use getLeft() and getTop() of imageView
// if getLeft() works you have the leftX coordinate of image already.
// do same with height
// also get the screen Width and Height by display.getWidth() (depreciated)
// Or the newer code display.getSize()
leftX = (screenwidth - imgWidth) / 2;
rightx = leftx + imgWidth;
}
Notice that this code only works when the image is displayed at the horizontal center of the screen. Or use top and bottom if the image is displayed at the vertical center of the screen.