I'm using "subsampling scale image view" in my app and I would like to dynamically add marker pins to a PinView when the user does a long click on it. The marker pin should appear at the clicked position.
I achieved that a marker pin appears after a long click, but at a wrong position. Here is the onCreateView method of my Fragment:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
pinCounter=0;
View rootView = inflater.inflate(R.layout.fragment_edit_plan, container, false);
src = getArguments().getString("src");
imageView = (PinView)rootView.findViewById(R.id.imageView);
imageView.setImage(ImageSource.uri(src));
imageView.isLongClickable();
imageView.isClickable();
imageView.hasOnClickListeners();
MapPins = new ArrayList();
imageView.setPins(MapPins);
imageView.setOnLongClickListener(this);
imageView.setOnTouchListener(this);
return rootView;
}
The Listener-Methods:
@Override
public boolean onLongClick(View view) {
if (view.getId() == R.id.imageView) {
Toast.makeText(getActivity(), "Long clicked "+lastKnownX+" "+lastKnownY, Toast.LENGTH_SHORT).show();
Log.d("EditPlanFragment","Scale "+imageView.getScale());
MapPins.add(new MapPin(lastKnownX,lastKnownY,pinCounter));
imageView.setPins(MapPins);
imageView.post(new Runnable(){
public void run(){
imageView.getRootView().postInvalidate();
}
});
return true;
}
return false;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (v.getId()== R.id.imageView && event.getAction() == MotionEvent.ACTION_DOWN){
lastKnownX= event.getX();
lastKnownY= event.getY();
}
return false;
}
My XML-File:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="50dp"
>
<com.example.viktoriabock.phoenixversion1.PinView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:longClickable="true"
/>
And the PinView-Class:
public class PinView extends SubsamplingScaleImageView {
private PointF sPin;
ArrayList<MapPin> mapPins;
ArrayList<DrawPin> drawnPins;
Context context;
String tag = getClass().getSimpleName();
public PinView(Context context) {
this(context, null);
this.context = context;
}
public PinView(Context context, AttributeSet attr) {
super(context, attr);
this.context = context;
initialise();
}
public void setPins(ArrayList<MapPin> mapPins) {
this.mapPins = mapPins;
initialise();
invalidate();
}
public void setPin(PointF pin) {
this.sPin = pin;
}
public PointF getPin() {
return sPin;
}
private void initialise() {
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Don't draw pin before image is ready so it doesn't move around during setup.
if (!isReady()) {
return;
}
drawnPins = new ArrayList<>();
Paint paint = new Paint();
paint.setAntiAlias(true);
float density = getResources().getDisplayMetrics().densityDpi;
for (int i = 0; i < mapPins.size(); i++) {
MapPin mPin = mapPins.get(i);
//Bitmap bmpPin = Utils.getBitmapFromAsset(context, mPin.getPinImgSrc());
Bitmap bmpPin = BitmapFactory.decodeResource(this.getResources(), R.drawable.pushpin_blue);
float w = (density / 420f) * bmpPin.getWidth();
float h = (density / 420f) * bmpPin.getHeight();
bmpPin = Bitmap.createScaledBitmap(bmpPin, (int) w, (int) h, true);
PointF vPin = sourceToViewCoord(mPin.getPoint());
//in my case value of point are at center point of pin image, so we need to adjust it here
float vX = vPin.x - (bmpPin.getWidth() / 2);
float vY = vPin.y - bmpPin.getHeight();
canvas.drawBitmap(bmpPin, vX, vY, paint);
//add added pin to an Array list to get touched pin
DrawPin dPin = new DrawPin();
dPin.setStartX(mPin.getX() - w / 2);
dPin.setEndX(mPin.getX() + w / 2);
dPin.setStartY(mPin.getY() - h / 2);
dPin.setEndY(mPin.getY() + h / 2);
dPin.setId(mPin.getId());
drawnPins.add(dPin);
}
}
public int getPinIdByPoint(PointF point) {
for (int i = drawnPins.size() - 1; i >= 0; i--) {
DrawPin dPin = drawnPins.get(i);
if (point.x >= dPin.getStartX() && point.x <= dPin.getEndX()) {
if (point.y >= dPin.getStartY() && point.y <= dPin.getEndY()) {
return dPin.getId();
}
}
}
return -1; //negative no means no pin selected
}
}
You're collecting the screen coordinates of the long press, then in the custom view you're converting them from source coordinates to screen coordinates. You need to convert the event coordinates into source coordinates when you get the tap event, using the view's viewToSourceCoord
method.
There's an easier way to do this than using your lastKnown
values - use your own GestureDetector
.
For a full working example of a long press gesture detector, see the AdvancedEventHandlingActivity
sample class.