Is there any way to place the image on the last line of the TextView
I use setCompoundDrawablesWithIntrinsicBounds. Some ideas about it will be good.
Also i need to handle clicks on my image only. This behavior is represented by the following code :
textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.info, 0);
textView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP) {
if(event.getRawX() >= textView.getRight() - textView.getTotalPaddingRight()){
listener.onAddressClicked(livingAreas);
return true;
}
}
return true;
}
});
With Text view you can set a drawable on a determinated position.
You can use this xml example:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableBottom="#drawable/image"
android:text="blablabla"/>
Programmatically:
public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom)
text_view.setCompoundDrawables(null, null, null, getDrawable(drawable_image_id));
Use SpannableString so that you can include image in your text. For code sample, check this question and answer
Related
so I made an app that draws bitmap into canvas as a template, and I want the user to be able add images from gallery then scale, position and rotate. currently stuck in positioning the imageView properly, when I drag and drop in different position the imageView gets little off the position I placed in.
here is the xml of my project to demonstorate the layout structure:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".search.CreateActivity">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
tools:layout_editor_absoluteX="283dp">
<!-- reault_layout contains layout and imageView:
layout for canvas the contains bitmap
layout for the image added by the user -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/result_layout"
android:layout_width="512dp"
android:layout_height="512dp"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/canvas_layout"
android:layout_width="512dp"
android:layout_height="512dp">
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:id="#+id/iv_promo"
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="..."/>
</androidx.constraintlayout.widget.ConstraintLayout>
</HorizontalScrollView>
<!-- this button to add the image -->
<Button
android:id="#+id/add_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<!-- this button saves whatever in the result layout as image in internal storage-->
<Button
android:id="#+id/save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>
another issue is, the imageView can not be placed at the far right when I scroll, it's only placed within the visible area of the screen before scrolling.
I set the canvas layout width and height to 512dp because its the actual drawable is 512dpx512dp and if I set it to match_parent it might get smaller and harder to place small images in certain areas inside of the template.
For the template to be drawn I placed these two lines inside onCreate();
create_canvas = new CreateCanvas(this);
nCanvas_layout.addView(create_canvas);
I did not use setContentView(); to not draw the template on the entire layout, so this will draw inside the canvas_layout
here is the CreateCanvas class that draws the template:
public class CreateCanvas extends View {
Drawable temp_drawable
Bitmap temp_bitmap
public CreateCanvas(Context context) {
super(context);
this.context = context;
// DRAWABLE OF THE TEMPLATE
temp_drawable = ContextCompat.getDrawable(context, R.drawable.ic_template_master);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//drawableToBitmap() IS A CUSTOM METHOD TO CONVERT THE DRAWABLE TEMPLATE TO BITMAP
// RETURNS BITMAP
temp_bitmap = drawableToBitmap(temp_drawable);
// DRAWS THE TEMPLATE INTO CANVAS
canvas.drawBitmap(temp_bitmap, left, top, null);
}
// THE CUSTOM METHOD TO CONVERT DRAWABLE TO BITMAP
public Bitmap drawableToBitmap(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable)drawable).getBitmap();
}
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
bitmap.setHeight(getWidth());
bitmap.setWidth(getWidth());
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
}
the above class helps in drawing the template into canvas_layout, below canvas_layout there's animageView where the user can upload image from internal storage.
I have added these implements to get user touch events
implements View.OnTouchListener, View.OnDragListener, GestureDetector.OnGestureListener
then got these #overrides methods:
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
// the imageView that host the image added by user: iv_promo
if (view.getId() == R.id.iv_promo)
{
nGestureDetector.onTouchEvent(motionEvent);
return true;
}
return true;
}
#Override
public boolean onDown(MotionEvent motionEvent) {return false;}
#Override
public void onShowPress(MotionEvent motionEvent) {}
#Override
public boolean onSingleTapUp(MotionEvent motionEvent) {return false;}
#Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {return false;}
#Override
public void onLongPress(MotionEvent motionEvent) {
View.DragShadowBuilder builder = new View.DragShadowBuilder(nIv_promo);
nIv_promo.startDragAndDrop(null, builder, null, 0);
builder.getView().setOnDragListener(this);
}
#Override
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {return false;}
#Override
public boolean onDrag(View view, DragEvent dragEvent) {
// I was trying thses outside if-statment to check if its working, but did not work
//nIv_promo.setX(dragEvent.getX()-(nIv_promo.getWidth()/2f));
//nIv_promo.setY(dragEvent.getY()-(nIv_promo.getHeight()/2f));
if (dragEvent.getAction() == DragEvent.ACTION_DRAG_STARTED)
{
// . . .
}
if (dragEvent.getAction() == DragEvent.ACTION_DRAG_ENTERED)
{
// . . .
}
if (dragEvent.getAction() == DragEvent.ACTION_DRAG_LOCATION)
{
//nIv_promo.setX(dragEvent.getX());
//nIv_promo.setY(dragEvent.getY());
}
if (dragEvent.getAction() == DragEvent.ACTION_DRAG_EXITED)
{
// . . .
}
if (dragEvent.getAction() == DragEvent.ACTION_DROP)
{
// . . .
}
if (dragEvent.getAction() == DragEvent.ACTION_DRAG_ENDED)
{
nIv_promo.setX(dragEvent.getX()-(nIv_promo.getWidth()/2f));
nIv_promo.setY(dragEvent.getY()-(nIv_promo.getHeight()/2f));
}
return true;
}
I did not like the DragShadowBuilder(), I wanted to drag the the actual imageView
and drop on different position on the screen. but Im too consumed by the placing the imageView accurately. if possible please help me with that too
Solved it by using emandt's suggestion. My personal Solution added below.
I'm using Android Studio for this.
I searched for solutions but couldn't find anything resembling this.
I want to know on which ImageView an UP action occurs while starting the DOWN action on a different ImageView (to eventually be able to drag one image over the other and make it snap to the same position by getting the position of the image I dragged over).
My example has two ImageViews with the id imageView (left) and imageView2(right).
In my example I'm not dragging anything yet, I just want to touch the left image, see "Action was down" in the log and lift the finger over the right image showing "Action was up2".
I don't know if this is easily possible.
As far as I can tell from testing, the MotionEvent.ACTION_UP only fires for an ImageView when you also pressed down on it beforehand. So when I release on top of imageView2 it only shows "Action was up" from the left image.
I wondered if it was possible by playing with return false, since the return value tells if an ActionEvent is consumed so I thought if the UP event of imageView returns false, maybe it does trigger the UP event of imageView2 but no. (Either complete misunderstanding on my part or it doesn't recognise UP on the second because it didn't start with a DOWN and MotionEvents probably always have to start with a DOWN).
public class MainActivity extends Activity {
ImageView imageView;
ImageView imageView2;
String DEBUG_TAG = "action";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.imageView);
imageView2 = findViewById(R.id.imageView2);
imageView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
//int action = MotionEventCompat.getActionMasked(event);
int action = event.getActionMasked();
switch(action) {
case (MotionEvent.ACTION_DOWN) :
Log.d(DEBUG_TAG,"Action was DOWN"+v.toString());
return true;
case (MotionEvent.ACTION_MOVE) :
//Log.d(DEBUG_TAG,"Action was MOVE");
return true;
case (MotionEvent.ACTION_UP) :
Log.d(DEBUG_TAG,"Action was UP"+v.toString());
return false;
default :
//return true;
}
return true;
}
});
imageView2.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
//int action = MotionEventCompat.getActionMasked(event);
int action = event.getActionMasked();
switch(action) {
case (MotionEvent.ACTION_DOWN) :
Log.d(DEBUG_TAG,"Action was DOWN2"+v.toString());
return true;
case (MotionEvent.ACTION_MOVE) :
//Log.d(DEBUG_TAG,"Action was MOVE");
return true;
case (MotionEvent.ACTION_UP) :
Log.d(DEBUG_TAG,"Action was UP2"+v.toString());
return true;
default :
//return true;
}
return true;
}
});
}
}
If there is no simple way to do this, I'm thinking about solving this mathematically, but maybe some of you can help.
So my question is, is there a way to recognise an UP action on a second ImageView while currently being in a MotionEvent of another ImageView?
SOLUTION (see emandt's answer)
I ditched the second OnClickListener because I realised that the 2nd image doesn't need any, I just need its position.
Added this method:
#Nullable
private View getDroppedView(View droppedView, int x, int y, List<View> arrayOfPossibilities) {
Rect cVisibleBoundsRect = new Rect();
for (View cView : arrayOfPossibilities) {
//if currently iterated view doesn't have values for getGlobalVisibleRect, skip the .contains part
//ignore the item which is your current active item (which would potentially be dropped)
//getGlobalVisibleRect sets cVisibleBoundsRect immediately to the Rect given as parameter
if (!cView.getGlobalVisibleRect(cVisibleBoundsRect)||(cView.equals(droppedView))) continue;
if (cVisibleBoundsRect.contains(x, y)) {
Log.d(DEBUG_TAG,"Found something");
//THIS "cView" IS THE VIEW WHERE YOU RELEASED THE FINGER
return cView;
}
}
Log.d(DEBUG_TAG,"Found nothing");
return null;
}
And added this in onUP:
case (MotionEvent.ACTION_UP) :
View dropTarget;
Log.d(DEBUG_TAG,"Action was UP"+v.toString());
dropTarget = getDroppedView(v, (int)event.getRawX(), (int)event.getRawY(), listOfViews);
if (dropTarget != null){
v.setX(dropTarget.getX());
v.setY(dropTarget.getY());
}
I think you want to know which is the View where you release the finger from the screen, am I right?
To do this you can use the same "View.OnTouchListener()" for all of your Views and in the ACTION_UP you have to call a new method similar to this (pseudo-code):
....
case (MotionEvent.ACTION_UP) :
View[] cArrayOfPossibileViews = new View[]{ findViewById(IMAGE_1), findViewById(IMAGE2) }
getDroppedView(v, event.getRawX(), event.getRawY(), cArrayOfPossibileViews);
break;
}
....
#Nullable
private View getDroppedView(View view, int x, int y, View[] arrayOfPossibilities) {
Rect cVisibleBoundsRect = new Rect();
for (View cView : arrayOfPossibilities) {
if (!cView.getGlobalVisibleRect(cVisibleBoundsRect)) continue;
if (cVisibleBoundsRect.contains(x, y)) {
//THIS "cView" IS THE VIEW WHERE YOU RELEASED THE FINGER
return cView;
}
}
return null;
}
This method get View bounds and compare them avains X and Y of your Touch Event. If X and Y are contained inside a View bounds it means that View is the one you need.
I'm still incredible new to android programming (like 3 weeks old) but I'm slowly getting the hang of things. I've looked on different websites for an answer but I haven't found anything yet.
My Java code so far (at least what's relevant):
view = (ImageView)findViewById(R.id.imageView3);
backgroundImageName = String.valueOf(view.getTag());
view.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
int myNewX = (int)event.getX(); //this line and the next get the X & Y coordinates of wherever the mouse clicked
int myNewY = (int)event.getY();
Button button = (Button)findViewById(R.id.add_text);
// find a way to keep the button within the borders of the white square
if (event.getAction() == MotionEvent.ACTION_UP) { //checks if the mouse click was released
button.setVisibility(View.VISIBLE);
button.setX(myNewX - 160); //this line and the next set the coordinates of the button (plus the adjustment)
button.setY(myNewY + 70); //to make the button by above and in the middle of where the mouse clicked
}
else
{
button.setVisibility(View.INVISIBLE);
}
return true;
}
});
and my XML code so far (at least what's relevant):
<ImageView
android:id="#+id/imageView3"
android:layout_width="450dp"
android:layout_height="450dp"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:contentDescription="#string/white_background"
android:tag="white"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="#+id/button"
app:layout_constraintLeft_toLeftOf="#+id/imageView2"
app:layout_constraintRight_toRightOf="#+id/imageView2"
app:layout_constraintTop_toTopOf="#+id/textView"
app:srcCompat="#mipmap/white" />
<Button
android:id="#+id/add_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/add_text"
android:visibility="invisible"
tools:ignore="MissingConstraints"
tools:layout_editor_absoluteY="71dp"
tools:layout_editor_absoluteX="16dp" />
What I'm trying to do is keep the button within the boundaries shown in the picture but I'm not sure how to do this. I've got an idea of how it's supposed to work which is:
If mouseclick (the event) is outside the border {
set the x coordinate of the button to the edge of the border
and the same for the y coordinate
}
I've been trying to get it to properly work but it simply refuses to. If anyone can help me figure out the code I need to make it work, I would really appreciate the help.
Thank you
First create a boundary check and then perform your task:
view.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
//obtain the boundaries of the view
if(event.getAction() == MotionEvent.ACTION_DOWN){
rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
}
if (event.getAction() == MotionEvent.ACTION_UP){
//Catches out of boundary user's movement.
if(!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() +
(int) event.getY())){
button.setVisibility(View.VISIBLE);
//perform your other calculations
}
return true;
}
});
Note: From API 14 you can use: Android MotionEvent's ACTION_HOVER_ENTER:
https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER
I have a TextView that contains a DrawableRight, what I want to do is detecting when the user presses that icon in drawableRight, is that possible ? and if it is how can I do it ?
PS: I am working inside a fragment
TextView XML
<TextView
android:id="#+id/mTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textIsSelectable="false"
android:textSize="22dp"
android:drawableRight="#mipmap/icn" //this is the drawable
/>
mTitle.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
final int DRAWABLE_RIGHT = 2;
if (event.getAction() == MotionEvent.ACTION_UP) {
if (event.getRawX() >= (mTitle.getRight() - mTitle.getCompoundDrawables()[DRAWABLE_RIGHT].getBounds().width())) {
//drawable pressed
return true;
}
}
return false;
}
});
I have this code in the ACTION_DOWN of my onTouch(View v) method as follows...
if (event.getAction() == MotionEvent.ACTION_DOWN) {
DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
view.startDrag(null, shadowBuilder, view, 0);
myOnClickListener.onClick(view);
return true;
}
Now my view in this case is a ChessPiece. I have chessPiece.setBackground(R.color.black) and chessPiece.setImageBitmap(pawnBitmap).
Currently when I drag the chess piece, it drags the background color with it, which I dont want. So is there anyway to remove the background of the view when it's dragging so that the DragShadow is just of the bitmap?
You have to create your own dragshadow. This example helped me when I wanted to play with the dragshadow: http://lemonycode.blogspot.be/2012/11/using-custom-dragshadowbuidler-in.html
As you can see, there are two important methods:
public void onProvideShadowMetrics(Point shadowSize, Point touchPoint)
and:
public void onDrawShadow(Canvas canvas)
The first method is used to size the dragshadow and the second to actually draw your shadow. In your case, your chess piece. You could use
Bitmap bitmap = ((BitmapDrawable)image.getDrawable()).getBitmap();
to get the bitmap and alter it before drawing it on the canvas.
On OnDragListener()
private final class MyDragListener implements OnDragListener {
#Override
public boolean onDrag(View v, DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
getResources().getDrawable(android.graphics.Color.TRANSPARENT);
break;
....
}
return true;
}
}
This should work. Good luck!